wanshenmean
2026-03-13 adf41ccad7f0abd082d899c390995a0a2e584044
docs: add S7 PLC simulator implementation plan

- Chunk 1: Project infrastructure (solution, projects, packages)
- Chunk 2: Core enums and entities (PLC type, instance config/state)
- Chunk 3: Memory storage implementation (M/DB/I/Q/T/C regions)
- TDD approach with comprehensive unit tests
- Bite-sized tasks with exact file paths and commands

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
已添加1个文件
2478 ■■■■■ 文件已修改
Code/WCS/WIDESEAWCS_Server/docs/superpowers/plans/2026-03-13-s7-plc-simulator-implementation.md 2478 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/docs/superpowers/plans/2026-03-13-s7-plc-simulator-implementation.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,2478 @@
# S7 PLC模拟器实施计划
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
**目标:** æž„建一个基于HSL Communication库的西门子S7 PLC模拟器系统,支持多实例、Web管理界面和数据持久化。
**架构:** é‡‡ç”¨DDD分层架构,Core层实现领域逻辑,Application层提供应用服务,Server层提供Web API,Web层提供管理界面。使用HSL Communication库实现S7服务器,SignalR实现实时状态推送。
**技术栈:** ASP.NET Core 6.0, HslCommunication 12.6.3, SignalR, Razor Pages, Serilog, AutoMapper, xUnit
---
## æ–‡ä»¶ç»“构映射
### Core项目
| æ–‡ä»¶ | èŒè´£ |
|------|------|
| `Enums/SiemensPLCType.cs` | PLC型号枚举 |
| `Enums/InstanceStatus.cs` | å®žä¾‹çŠ¶æ€æžšä¸¾ |
| `Entities/InstanceConfig.cs` | å®žä¾‹é…ç½®å®žä½“ |
| `Entities/InstanceState.cs` | å®žä¾‹çŠ¶æ€å®žä½“ |
| `Entities/S7ClientConnection.cs` | å®¢æˆ·ç«¯è¿žæŽ¥å®žä½“ |
| `Entities/MemoryRegionConfig.cs` | å†…存区域配置 |
| `Interfaces/IMemoryRegion.cs` | å†…存区域接口 |
| `Interfaces/IMemoryStore.cs` | å†…存存储接口 |
| `Interfaces/IS7ServerInstance.cs` | æœåŠ¡å™¨å®žä¾‹æŽ¥å£ |
| `Interfaces/ISimulatorInstanceManager.cs` | å®žä¾‹ç®¡ç†å™¨æŽ¥å£ |
| `Interfaces/IPersistenceService.cs` | æŒä¹…化服务接口 |
| `Memory/MemoryRegion.cs` | å†…存区域基类 |
| `Memory/MRegion.cs` | M区实现 |
| `Memory/DBRegion.cs` | DB区实现 |
| `Memory/IRegion.cs` | I区实现 |
| `Memory/QRegion.cs` | Q区实现 |
| `Memory/TRegion.cs` | T区实现 |
| `Memory/CRegion.cs` | C区实现 |
| `Memory/MemoryStore.cs` | å†…存存储实现 |
| `Persistence/FilePersistenceService.cs` | æ–‡ä»¶æŒä¹…化服务 |
| `Server/S7ServerInstance.cs` | S7服务器实例实现 |
| `Manager/SimulatorInstanceManager.cs` | å®žä¾‹ç®¡ç†å™¨å®žçް |
### Application项目
| æ–‡ä»¶ | èŒè´£ |
|------|------|
| `DTOs/InstanceDTO.cs` | å®žä¾‹æ•°æ®ä¼ è¾“对象 |
| `DTOs/CreateInstanceDTO.cs` | åˆ›å»ºå®žä¾‹DTO |
| `DTOs/UpdateInstanceDTO.cs` | æ›´æ–°å®žä¾‹DTO |
| `DTOs/InstanceDetailDTO.cs` | å®žä¾‹è¯¦æƒ…DTO |
| `DTOs/MemoryReadDTO.cs` | å†…存读取DTO |
| `DTOs/MemoryWriteDTO.cs` | å†…存写入DTO |
| `DTOs/ClientConnectionDTO.cs` | å®¢æˆ·ç«¯è¿žæŽ¥DTO |
| `DTOs/InstanceStateEventArgs.cs` | çŠ¶æ€äº‹ä»¶å‚æ•° |
| `DTOs/ClientConnectionEventArgs.cs` | å®¢æˆ·ç«¯è¿žæŽ¥äº‹ä»¶å‚æ•° |
| `Services/SimulatorInstanceAppService.cs` | å®žä¾‹åº”用服务 |
| `Services/MemoryAppService.cs` | å†…存应用服务 |
| `Services/ClientAppService.cs` | å®¢æˆ·ç«¯åº”用服务 |
| `Profiles/MappingProfile.cs` | AutoMapper配置 |
### Server项目
| æ–‡ä»¶ | èŒè´£ |
|------|------|
| `Controllers/SimulatorInstancesController.cs` | å®žä¾‹åˆ—表API |
| `Controllers/SimulatorInstanceController.cs` | å®žä¾‹æŽ§åˆ¶API |
| `Controllers/MemoryController.cs` | å†…存操作API |
| `Controllers/ClientsController.cs` | å®¢æˆ·ç«¯ç®¡ç†API |
| `Hubs/SimulatorHub.cs` | SignalR实时推送 |
| `Infrastructure/DependencyInjection.cs` | DI配置 |
| `Infrastructure/Middleware/ExceptionMiddleware.cs` | å¼‚常处理中间件 |
| `Program.cs` | ç¨‹åºå…¥å£ |
### Web项目
| æ–‡ä»¶ | èŒè´£ |
|------|------|
| `Pages/Index.cshtml` | å®žä¾‹åˆ—表页 |
| `Pages/Index.cshtml.cs` | å®žä¾‹åˆ—表页模型 |
| `Pages/Create.cshtml` | åˆ›å»ºå®žä¾‹é¡µ |
| `Pages/Create.cshtml.cs` | åˆ›å»ºå®žä¾‹é¡µæ¨¡åž‹ |
| `Pages/Edit.cshtml` | ç¼–辑实例页 |
| `Pages/Edit.cshtml.cs` | ç¼–辑实例页模型 |
| `Pages/Details.cshtml` | å®žä¾‹è¯¦æƒ…页 |
| `Pages/Details.cshtml.cs` | å®žä¾‹è¯¦æƒ…页模型 |
| `Pages/Shared/_Layout.cshtml` | å¸ƒå±€é¡µ |
| `wwwroot/css/site.css` | æ ·å¼æ–‡ä»¶ |
| `wwwroot/js/site.js` | JavaScript文件 |
### Tests项目
| æ–‡ä»¶ | èŒè´£ |
|------|------|
| `Memory/MRegionTests.cs` | M区单元测试 |
| `Memory/DBRegionTests.cs` | DB区单元测试 |
| `Memory/MemoryStoreTests.cs` | å†…存存储单元测试 |
| `Server/S7ServerInstanceTests.cs` | æœåŠ¡å™¨å®žä¾‹å•å…ƒæµ‹è¯• |
| `Persistence/FilePersistenceServiceTests.cs` | æŒä¹…化服务单元测试 |
---
## Chunk 1: é¡¹ç›®åŸºç¡€è®¾æ–½
### Task 1: åˆ›å»ºè§£å†³æ–¹æ¡ˆå’Œé¡¹ç›®ç»“æž„
- [ ] **Step 1: åœ¨WCS目录下创建S7模拟器解决方案**
```bash
cd D:\Git\ShanMeiXinNengYuan\Code\WCS
dotnet new sln -n WIDESEAWCS_S7Simulator
```
预期输出: åˆ›å»º `WIDESEAWCS_S7Simulator.sln`
- [ ] **Step 2: åˆ›å»ºCore项目**
```bash
cd WIDESEAWCS_S7Simulator
dotnet new classlib -n WIDESEAWCS_S7Simulator.Core -f net6.0
dotnet sln add WIDESEAWCS_S7Simulator.Core/WIDESEAWCS_S7Simulator.Core.csproj
```
- [ ] **Step 3: åˆ›å»ºApplication项目**
```bash
dotnet new classlib -n WIDESEAWCS_S7Simulator.Application -f net6.0
dotnet sln add WIDESEAWCS_S7Simulator.Application/WIDESEAWCS_S7Simulator.Application.csproj
```
- [ ] **Step 4: åˆ›å»ºServer项目**
```bash
dotnet new webapi -n WIDESEAWCS_S7Simulator.Server -f net6.0
dotnet sln add WIDESEAWCS_S7Simulator.Server/WIDESEAWCS_S7Simulator.Server.csproj
```
- [ ] **Step 5: åˆ›å»ºWeb项目**
```bash
dotnet new webapp -n WIDESEAWCS_S7Simulator.Web -f net6.0
dotnet sln add WIDESEAWCS_S7Simulator.Web/WIDESEAWCS_S7Simulator.Web.csproj
```
- [ ] **Step 6: åˆ›å»ºTests项目**
```bash
dotnet new xunit -n WIDESEAWCS_S7Simulator.UnitTests -f net6.0
dotnet sln add WIDESEAWCS_S7Simulator.UnitTests/WIDESEAWCS_S7Simulator.UnitTests.csproj
```
- [ ] **Step 7: æ·»åŠ é¡¹ç›®å¼•ç”¨**
```bash
cd WIDESEAWCS_S7Simulator.Application
dotnet add reference ../WIDESEAWCS_S7Simulator.Core/WIDESEAWCS_S7Simulator.Core.csproj
cd ../WIDESEAWCS_S7Simulator.Server
dotnet add reference ../WIDESEAWCS_S7Simulator.Core/WIDESEAWCS_S7Simulator.Core.csproj
dotnet add reference ../WIDESEAWCS_S7Simulator.Application/WIDESEAWCS_S7Simulator.Application.csproj
cd ../WIDESEAWCS_S7Simulator.Web
dotnet add reference ../WIDESEAWCS_S7Simulator.Application/WIDESEAWCS_S7Simulator.Application.csproj
cd ../WIDESEAWCS_S7Simulator.UnitTests
dotnet add reference ../WIDESEAWCS_S7Simulator.Core/WIDESEAWCS_S7Simulator.Core.csproj
```
- [ ] **Step 8: æ·»åŠ NuGet包到Core项目**
```bash
cd ../WIDESEAWCS_S7Simulator.Core
dotnet add package HslCommunication --version 12.6.3
dotnet add package Microsoft.Extensions.Logging.Abstractions
dotnet add package Microsoft.Extensions.Configuration.Abstractions
dotnet add package Newtonsoft.Json
```
- [ ] **Step 9: æ·»åŠ NuGet包到Server项目**
```bash
cd ../WIDESEAWCS_S7Simulator.Server
dotnet add package Serilog.AspNetCore
dotnet add package AutoMapper
dotnet add package Microsoft.AspNetCore.SignalR
```
- [ ] **Step 10: æ·»åŠ NuGet包到Web项目**
```bash
cd ../WIDESEAWCS_S7Simulator.Web
dotnet add package Microsoft.AspNetCore.SignalR.Client
```
- [ ] **Step 11: éªŒè¯è§£å†³æ–¹æ¡ˆæž„建**
```bash
cd ..
dotnet build
```
预期输出: æž„建成功
- [ ] **Step 12: æäº¤é¡¹ç›®ç»“æž„**
```bash
git add .
git commit -m "feat: create S7 simulator solution structure
- Add Core, Application, Server, Web projects
- Configure project references
- Add required NuGet packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Chunk 2: æ ¸å¿ƒæžšä¸¾å’Œå®žä½“
### Task 2: åˆ›å»ºæžšä¸¾ç±»åž‹
**Files:**
- Create: `WIDESEAWCS_S7Simulator.Core/Enums/SiemensPLCType.cs`
- Create: `WIDESEAWCS_S7Simulator.Core/Enums/InstanceStatus.cs`
- [ ] **Step 1: åˆ›å»ºPLC类型枚举**
```csharp
namespace WIDESEAWCS_S7Simulator.Core.Enums
{
    /// <summary>
    /// è¥¿é—¨å­PLC型号
    /// </summary>
    public enum SiemensPLCType
    {
        /// <summary>
        /// S7-200 Smart
        /// </summary>
        S7200Smart = 0,
        /// <summary>
        /// S7-1200
        /// </summary>
        S71200 = 1,
        /// <summary>
        /// S7-1500
        /// </summary>
        S71500 = 2,
        /// <summary>
        /// S7-300
        /// </summary>
        S7300 = 3,
        /// <summary>
        /// S7-400
        /// </summary>
        S7400 = 4
    }
}
```
- [ ] **Step 2: åˆ›å»ºå®žä¾‹çŠ¶æ€æžšä¸¾**
```csharp
namespace WIDESEAWCS_S7Simulator.Core.Enums
{
    /// <summary>
    /// S7服务器实例运行状态
    /// </summary>
    public enum InstanceStatus
    {
        /// <summary>
        /// å·²åœæ­¢
        /// </summary>
        Stopped = 0,
        /// <summary>
        /// å¯åЍ䏭
        /// </summary>
        Starting = 1,
        /// <summary>
        /// è¿è¡Œä¸­
        /// </summary>
        Running = 2,
        /// <summary>
        /// åœæ­¢ä¸­
        /// </summary>
        Stopping = 3,
        /// <summary>
        /// é”™è¯¯
        /// </summary>
        Error = 4
    }
}
```
- [ ] **Step 3: åˆ é™¤é»˜è®¤ç”Ÿæˆçš„Class1.cs文件**
```bash
rm WIDESEAWCS_S7Simulator.Core/Class1.cs
```
- [ ] **Step 4: éªŒè¯æž„建**
```bash
cd WIDESEAWCS_S7Simulator.Core
dotnet build
```
- [ ] **Step 5: æäº¤æžšä¸¾ç±»åž‹**
```bash
git add .
git commit -m "feat: add PLC type and instance status enums
- Add SiemensPLCType enum (S7-200/1200/1500/300/400)
- Add InstanceStatus enum (Stopped/Starting/Running/Stopping/Error)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
### Task 3: åˆ›å»ºæ ¸å¿ƒå®žä½“
**Files:**
- Create: `WIDESEAWCS_S7Simulator.Core/Entities/MemoryRegionConfig.cs`
- Create: `WIDESEAWCS_S7Simulator.Core/Entities/InstanceConfig.cs`
- Create: `WIDESEAWCS_S7Simulator.Core/Entities/S7ClientConnection.cs`
- Create: `WIDESEAWCS_S7Simulator.Core/Entities/InstanceState.cs`
- [ ] **Step 1: åˆ›å»ºå†…存区域配置实体**
```csharp
using Newtonsoft.Json;
namespace WIDESEAWCS_S7Simulator.Core.Entities
{
    /// <summary>
    /// å†…存区域配置
    /// </summary>
    public class MemoryRegionConfig
    {
        /// <summary>
        /// M区大小(字节),默认1KB
        /// </summary>
        [JsonProperty("mRegionSize")]
        public int MRegionSize { get; set; } = 1024;
        /// <summary>
        /// DB块数量,默认100个
        /// </summary>
        [JsonProperty("dbBlockCount")]
        public int DBBlockCount { get; set; } = 100;
        /// <summary>
        /// æ¯ä¸ªDB块大小(字节),默认1KB
        /// </summary>
        [JsonProperty("dbBlockSize")]
        public int DBBlockSize { get; set; } = 1024;
        /// <summary>
        /// I区大小(字节),默认256字节
        /// </summary>
        [JsonProperty("iRegionSize")]
        public int IRegionSize { get; set; } = 256;
        /// <summary>
        /// Q区大小(字节),默认256字节
        /// </summary>
        [JsonProperty("qRegionSize")]
        public int QRegionSize { get; set; } = 256;
        /// <summary>
        /// T区数量,默认64个
        /// </summary>
        [JsonProperty("tRegionCount")]
        public int TRegionCount { get; set; } = 64;
        /// <summary>
        /// C区数量,默认64个
        /// </summary>
        [JsonProperty("cRegionCount")]
        public int CRegionCount { get; set; } = 64;
    }
}
```
- [ ] **Step 2: åˆ›å»ºå®žä¾‹é…ç½®å®žä½“**
```csharp
using Newtonsoft.Json;
using WIDESEAWCS_S7Simulator.Core.Enums;
namespace WIDESEAWCS_S7Simulator.Core.Entities
{
    /// <summary>
    /// S7服务器实例配置
    /// </summary>
    public class InstanceConfig
    {
        /// <summary>
        /// å®žä¾‹å”¯ä¸€æ ‡è¯†
        /// </summary>
        [JsonProperty("id")]
        public string Id { get; set; } = string.Empty;
        /// <summary>
        /// å®žä¾‹åç§°
        /// </summary>
        [JsonProperty("name")]
        public string Name { get; set; } = string.Empty;
        /// <summary>
        /// PLC型号
        /// </summary>
        [JsonProperty("plcType")]
        public SiemensPLCType PLCType { get; set; }
        /// <summary>
        /// ç›‘听端口
        /// </summary>
        [JsonProperty("port")]
        public int Port { get; set; }
        /// <summary>
        /// HSL激活码
        /// </summary>
        [JsonProperty("activationKey")]
        public string ActivationKey { get; set; } = string.Empty;
        /// <summary>
        /// æ˜¯å¦è‡ªåŠ¨å¯åŠ¨
        /// </summary>
        [JsonProperty("autoStart")]
        public bool AutoStart { get; set; }
        /// <summary>
        /// å†…存区域配置
        /// </summary>
        [JsonProperty("memoryConfig")]
        public MemoryRegionConfig MemoryConfig { get; set; } = new();
    }
}
```
- [ ] **Step 3: åˆ›å»ºå®¢æˆ·ç«¯è¿žæŽ¥å®žä½“**
```csharp
namespace WIDESEAWCS_S7Simulator.Core.Entities
{
    /// <summary>
    /// S7客户端连接信息
    /// </summary>
    public class S7ClientConnection
    {
        /// <summary>
        /// å®¢æˆ·ç«¯å”¯ä¸€æ ‡è¯†
        /// </summary>
        public string ClientId { get; set; } = string.Empty;
        /// <summary>
        /// å®¢æˆ·ç«¯IP地址和端口
        /// </summary>
        public string RemoteEndPoint { get; set; } = string.Empty;
        /// <summary>
        /// è¿žæŽ¥æ—¶é—´
        /// </summary>
        public DateTime ConnectedTime { get; set; }
        /// <summary>
        /// æœ€åŽæ´»åŠ¨æ—¶é—´
        /// </summary>
        public DateTime LastActivityTime { get; set; }
    }
}
```
- [ ] **Step 4: åˆ›å»ºå®žä¾‹çŠ¶æ€å®žä½“**
```csharp
using WIDESEAWCS_S7Simulator.Core.Enums;
namespace WIDESEAWCS_S7Simulator.Core.Entities
{
    /// <summary>
    /// S7服务器实例状态
    /// </summary>
    public class InstanceState
    {
        /// <summary>
        /// å®žä¾‹ID
        /// </summary>
        public string InstanceId { get; set; } = string.Empty;
        /// <summary>
        /// è¿è¡ŒçŠ¶æ€
        /// </summary>
        public InstanceStatus Status { get; set; }
        /// <summary>
        /// å½“前连接的客户端数量
        /// </summary>
        public int ClientCount { get; set; }
        /// <summary>
        /// ç´¯è®¡å¤„理请求数
        /// </summary>
        public long TotalRequests { get; set; }
        /// <summary>
        /// å¯åŠ¨æ—¶é—´
        /// </summary>
        public DateTime? StartTime { get; set; }
        /// <summary>
        /// æœ€åŽæ´»åŠ¨æ—¶é—´
        /// </summary>
        public DateTime? LastActivityTime { get; set; }
        /// <summary>
        /// è¿žæŽ¥çš„客户端列表
        /// </summary>
        public List<S7ClientConnection> Clients { get; set; } = new();
        /// <summary>
        /// é”™è¯¯ä¿¡æ¯ï¼ˆå½“状态为Error时)
        /// </summary>
        public string? ErrorMessage { get; set; }
    }
}
```
- [ ] **Step 5: éªŒè¯æž„建**
```bash
cd WIDESEAWCS_S7Simulator.Core
dotnet build
```
- [ ] **Step 6: æäº¤æ ¸å¿ƒå®žä½“**
```bash
git add .
git commit -m "feat: add core entities
- Add MemoryRegionConfig for memory region sizes
- Add InstanceConfig for server configuration
- Add S7ClientConnection for client info
- Add InstanceState for server state tracking
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Chunk 3: å†…存存储实现
### Task 4: åˆ›å»ºå†…存区域接口和基类
**Files:**
- Create: `WIDESEAWCS_S7Simulator.Core/Interfaces/IMemoryRegion.cs`
- Create: `WIDESEAWCS_S7Simulator.Core/Memory/MemoryRegion.cs`
- [ ] **Step 1: åˆ›å»ºå†…存区域接口**
```csharp
namespace WIDESEAWCS_S7Simulator.Core.Interfaces
{
    /// <summary>
    /// å†…存区域接口
    /// </summary>
    public interface IMemoryRegion
    {
        /// <summary>
        /// åŒºåŸŸç±»åž‹ï¼ˆM/DB/I/Q/T/C)
        /// </summary>
        string RegionType { get; }
        /// <summary>
        /// åŒºåŸŸå¤§å°ï¼ˆå­—节)
        /// </summary>
        int Size { get; }
        /// <summary>
        /// è¯»å–字节数据
        /// </summary>
        /// <param name="offset">偏移量</param>
        /// <param name="length">长度</param>
        /// <returns>字节数组</returns>
        byte[] Read(ushort offset, ushort length);
        /// <summary>
        /// å†™å…¥å­—节数据
        /// </summary>
        /// <param name="offset">偏移量</param>
        /// <param name="data">数据</param>
        void Write(ushort offset, byte[] data);
        /// <summary>
        /// æ¸…空区域
        /// </summary>
        void Clear();
    }
}
```
- [ ] **Step 2: åˆ›å»ºå†…存区域基类**
```csharp
using System.Threading;
using WIDESEAWCS_S7Simulator.Core.Interfaces;
namespace WIDESEAWCS_S7Simulator.Core.Memory
{
    /// <summary>
    /// å†…存区域基类
    /// </summary>
    public abstract class MemoryRegion : IMemoryRegion
    {
        /// <summary>
        /// å†…存数据
        /// </summary>
        protected readonly byte[] _memory;
        /// <summary>
        /// è¯»å†™é”ï¼ˆæ”¯æŒå¹¶å‘访问)
        /// </summary>
        protected readonly ReaderWriterLockSlim _lock;
        /// <summary>
        /// åŒºåŸŸç±»åž‹
        /// </summary>
        public abstract string RegionType { get; }
        /// <summary>
        /// åŒºåŸŸå¤§å°ï¼ˆå­—节)
        /// </summary>
        public int Size { get; }
        /// <summary>
        /// æž„造函数
        /// </summary>
        /// <param name="size">区域大小</param>
        protected MemoryRegion(int size)
        {
            Size = size;
            _memory = new byte[size];
            _lock = new ReaderWriterLockSlim();
        }
        /// <summary>
        /// è¯»å–字节数据
        /// </summary>
        public virtual byte[] Read(ushort offset, ushort length)
        {
            _lock.EnterReadLock();
            try
            {
                if (offset + length > Size)
                    throw new ArgumentOutOfRangeException(
                        $"读取超出{RegionType}区范围: offset={offset}, length={length}, size={Size}");
                byte[] result = new byte[length];
                Array.Copy(_memory, offset, result, 0, length);
                return result;
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }
        /// <summary>
        /// å†™å…¥å­—节数据
        /// </summary>
        public virtual void Write(ushort offset, byte[] data)
        {
            _lock.EnterWriteLock();
            try
            {
                if (offset + data.Length > Size)
                    throw new ArgumentOutOfRangeException(
                        $"写入超出{RegionType}区范围: offset={offset}, length={data.Length}, size={Size}");
                Array.Copy(data, 0, _memory, offset, data.Length);
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }
        /// <summary>
        /// æ¸…空区域
        /// </summary>
        public virtual void Clear()
        {
            _lock.EnterWriteLock();
            try
            {
                Array.Clear(_memory, 0, Size);
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }
        /// <summary>
        /// é‡Šæ”¾èµ„源
        /// </summary>
        public virtual void Dispose()
        {
            _lock?.Dispose();
        }
    }
}
```
- [ ] **Step 3: éªŒè¯æž„建**
```bash
cd WIDESEAWCS_S7Simulator.Core
dotnet build
```
- [ ] **Step 4: æäº¤å†…存区域基类**
```bash
git add .
git commit -m "feat: add memory region interface and base class
- Add IMemoryRegion interface
- Add MemoryRegion base class with thread-safe read/write
- Support offset-based read/write operations
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
### Task 5: å®žçްM区(位存储器)
**Files:**
- Create: `WIDESEAWCS_S7Simulator.Core/Memory/MRegion.cs`
- Test: `WIDESEAWCS_S7Simulator.UnitTests/Memory/MRegionTests.cs`
- [ ] **Step 1: ç¼–写M区测试**
```csharp
using Xunit;
using WIDESEAWCS_S7Simulator.Core.Memory;
namespace WIDESEAWCS_S7Simulator.UnitTests.Memory
{
    public class MRegionTests
    {
        [Fact]
        public void Constructor_WithValidSize_CreatesRegion()
        {
            // Arrange & Act
            var region = new MRegion(1024);
            // Assert
            Assert.Equal("M", region.RegionType);
            Assert.Equal(1024, region.Size);
        }
        [Fact]
        public void Read_WithinBounds_ReturnsData()
        {
            // Arrange
            var region = new MRegion(1024);
            var testData = new byte[] { 0x12, 0x34, 0x56, 0x78 };
            region.Write(0, testData);
            // Act
            var result = region.Read(0, 4);
            // Assert
            Assert.Equal(testData, result);
        }
        [Fact]
        public void Read_OutOfBounds_ThrowsArgumentOutOfRange()
        {
            // Arrange
            var region = new MRegion(100);
            // Act & Assert
            Assert.Throws<ArgumentOutOfRangeException>(() => region.Read(0, 101));
        }
        [Fact]
        public void Write_WithinBounds_WritesData()
        {
            // Arrange
            var region = new MRegion(1024);
            var testData = new byte[] { 0xAA, 0xBB, 0xCC, 0xDD };
            // Act
            region.Write(100, testData);
            var result = region.Read(100, 4);
            // Assert
            Assert.Equal(testData, result);
        }
        [Fact]
        public void Write_OutOfBounds_ThrowsArgumentOutOfRange()
        {
            // Arrange
            var region = new MRegion(100);
            var testData = new byte[] { 0x01, 0x02 };
            // Act & Assert
            Assert.Throws<ArgumentOutOfRangeException>(() => region.Write(99, testData));
        }
        [Fact]
        public void ReadBit_ValidBit_ReturnsCorrectValue()
        {
            // Arrange
            var region = new MRegion(1024);
            region.Write(0, new byte[] { 0xFF }); // æ‰€æœ‰ä½ä¸º1
            // Act
            var result = region.ReadBit(0, 0);
            // Assert
            Assert.True(result);
        }
        [Fact]
        public void WriteBit_ValidBit_SetsCorrectValue()
        {
            // Arrange
            var region = new MRegion(1024);
            // Act
            region.WriteBit(0, 3, true);
            var result = region.ReadBit(0, 3);
            // Assert
            Assert.True(result);
        }
        [Fact]
        public void WriteBit_InvalidBitOffset_ThrowsArgumentOutOfRange()
        {
            // Arrange
            var region = new MRegion(1024);
            // Act & Assert
            Assert.Throws<ArgumentOutOfRangeException>(() => region.WriteBit(0, 8, true));
        }
        [Fact]
        public void Clear_ZerosAllMemory()
        {
            // Arrange
            var region = new MRegion(100);
            region.Write(0, new byte[] { 0xFF, 0xFF, 0xFF });
            // Act
            region.Clear();
            var result = region.Read(0, 3);
            // Assert
            Assert.Equal(new byte[] { 0, 0, 0 }, result);
        }
        [Fact]
        public void ConcurrentReadWrite_ThreadSafe()
        {
            // Arrange
            var region = new MRegion(1024);
            var exceptions = new System.Collections.Concurrent.ConcurrentBag<Exception>();
            var cts = new CancellationTokenSource();
            cts.CancelAfter(1000); // 1秒后取消
            // Act
            var writeTask = Task.Run(() =>
            {
                try
                {
                    var data = new byte[] { 0xAA, 0xBB };
                    while (!cts.Token.IsCancellationRequested)
                    {
                        region.Write(0, data);
                    }
                }
                catch (Exception ex)
                {
                    exceptions.Add(ex);
                }
            }, cts.Token);
            var readTask = Task.Run(() =>
            {
                try
                {
                    while (!cts.Token.IsCancellationRequested)
                    {
                        region.Read(0, 2);
                    }
                }
                catch (Exception ex)
                {
                    exceptions.Add(ex);
                }
            }, cts.Token);
            Task.WaitAll(writeTask, readTask);
            // Assert
            Assert.Empty(exceptions);
        }
    }
}
```
- [ ] **Step 2: è¿è¡Œæµ‹è¯•验证失败**
```bash
cd WIDESEAWCS_S7Simulator.UnitTests
dotnet test Memory/MRegionTests.cs -v n
```
预期输出: æµ‹è¯•失败(MRegion类不存在)
- [ ] **Step 3: å®žçްMRegionç±»**
```csharp
using System;
using WIDESEAWCS_S7Simulator.Core.Interfaces;
namespace WIDESEAWCS_S7Simulator.Core.Memory
{
    /// <summary>
    /// M区(位存储器/Merker)实现
    /// </summary>
    public class MRegion : MemoryRegion, IMemoryRegion
    {
        /// <summary>
        /// åŒºåŸŸç±»åž‹
        /// </summary>
        public override string RegionType => "M";
        /// <summary>
        /// æž„造函数
        /// </summary>
        /// <param name="size">区域大小(字节)</param>
        public MRegion(int size) : base(size)
        {
        }
        /// <summary>
        /// è¯»å–位
        /// </summary>
        /// <param name="byteOffset">字节偏移</param>
        /// <param name="bitOffset">位偏移(0-7)</param>
        /// <returns>位值</returns>
        public bool ReadBit(ushort byteOffset, byte bitOffset)
        {
            if (bitOffset > 7)
                throw new ArgumentOutOfRangeException(nameof(bitOffset), "位偏移必须在0-7之间");
            _lock.EnterReadLock();
            try
            {
                if (byteOffset >= Size)
                    throw new ArgumentOutOfRangeException(nameof(byteOffset), "字节偏移超出范围");
                return (_memory[byteOffset] & (1 << bitOffset)) != 0;
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }
        /// <summary>
        /// å†™å…¥ä½
        /// </summary>
        /// <param name="byteOffset">字节偏移</param>
        /// <param name="bitOffset">位偏移(0-7)</param>
        /// <param name="value">位值</param>
        public void WriteBit(ushort byteOffset, byte bitOffset, bool value)
        {
            if (bitOffset > 7)
                throw new ArgumentOutOfRangeException(nameof(bitOffset), "位偏移必须在0-7之间");
            _lock.EnterWriteLock();
            try
            {
                if (byteOffset >= Size)
                    throw new ArgumentOutOfRangeException(nameof(byteOffset), "字节偏移超出范围");
                if (value)
                    _memory[byteOffset] |= (byte)(1 << bitOffset);
                else
                    _memory[byteOffset] &= (byte)~(1 << bitOffset);
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }
        /// <summary>
        /// è¯»å–字(Word,2字节)
        /// </summary>
        public ushort ReadWord(ushort byteOffset)
        {
            var data = Read(byteOffset, 2);
            return (ushort)((data[0] << 8) | data[1]);
        }
        /// <summary>
        /// å†™å…¥å­—(Word,2字节)
        /// </summary>
        public void WriteWord(ushort byteOffset, ushort value)
        {
            var data = new byte[] { (byte)(value >> 8), (byte)(value & 0xFF) };
            Write(byteOffset, data);
        }
        /// <summary>
        /// è¯»å–双字(DWord,4字节)
        /// </summary>
        public uint ReadDWord(ushort byteOffset)
        {
            var data = Read(byteOffset, 4);
            return (uint)((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);
        }
        /// <summary>
        /// å†™å…¥åŒå­—(DWord,4字节)
        /// </summary>
        public void WriteDWord(ushort byteOffset, uint value)
        {
            var data = new byte[] {
                (byte)(value >> 24),
                (byte)((value >> 16) & 0xFF),
                (byte)((value >> 8) & 0xFF),
                (byte)(value & 0xFF)
            };
            Write(byteOffset, data);
        }
        /// <summary>
        /// è¯»å–整数(Int,2字节,有符号)
        /// </summary>
        public short ReadInt(ushort byteOffset)
        {
            return (short)ReadWord(byteOffset);
        }
        /// <summary>
        /// å†™å…¥æ•´æ•°ï¼ˆInt,2字节,有符号)
        /// </summary>
        public void WriteInt(ushort byteOffset, short value)
        {
            WriteWord(byteOffset, (ushort)value);
        }
        /// <summary>
        /// è¯»å–双整数(DInt,4字节,有符号)
        /// </summary>
        public int ReadDInt(ushort byteOffset)
        {
            return (int)ReadDWord(byteOffset);
        }
        /// <summary>
        /// å†™å…¥åŒæ•´æ•°ï¼ˆDInt,4字节,有符号)
        /// </summary>
        public void WriteDInt(ushort byteOffset, int value)
        {
            WriteDWord(byteOffset, (uint)value);
        }
        /// <summary>
        /// è¯»å–浮点数(Real,4字节)
        /// </summary>
        public float ReadReal(ushort byteOffset)
        {
            var bytes = Read(byteOffset, 4);
            return BitConverter.ToSingle(bytes, 0);
        }
        /// <summary>
        /// å†™å…¥æµ®ç‚¹æ•°ï¼ˆReal,4字节)
        /// </summary>
        public void WriteReal(ushort byteOffset, float value)
        {
            var bytes = BitConverter.GetBytes(value);
            Write(byteOffset, bytes);
        }
    }
}
```
- [ ] **Step 4: è¿è¡Œæµ‹è¯•验证通过**
```bash
dotnet test Memory/MRegionTests.cs -v n
```
预期输出: æ‰€æœ‰æµ‹è¯•通过
- [ ] **Step 5: æäº¤M区实现**
```bash
git add .
git commit -m "feat: implement M region (Merker memory)
- Add MRegion class with bit/word/dword/int/dint/real operations
- Support individual bit read/write with thread-safety
- Add comprehensive unit tests including concurrent access test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
### Task 6: å®žçްDB区(数据块)
**Files:**
- Create: `WIDESEAWCS_S7Simulator.Core/Memory/DBRegion.cs`
- Test: `WIDESEAWCS_S7Simulator.UnitTests/Memory/DBRegionTests.cs`
- [ ] **Step 1: ç¼–写DB区测试**
```csharp
using Xunit;
using WIDESEAWCS_S7Simulator.Core.Memory;
namespace WIDESEAWCS_S7Simulator.UnitTests.Memory
{
    public class DBRegionTests
    {
        [Fact]
        public void Constructor_WithValidParameters_CreatesRegion()
        {
            // Arrange & Act
            var region = new DBRegion(100, 1024);
            // Assert
            Assert.Equal("DB", region.RegionType);
            Assert.Equal(100 * 1024, region.Size);
        }
        [Fact]
        public void Read_ValidDBNumberAndOffset_ReturnsData()
        {
            // Arrange
            var region = new DBRegion(10, 1024);
            var testData = new byte[] { 0x12, 0x34, 0x56, 0x78 };
            region.Write(1, 0, testData);
            // Act
            var result = region.Read(1, 0, 4);
            // Assert
            Assert.Equal(testData, result);
        }
        [Fact]
        public void Read_InvalidDBNumber_ThrowsArgumentException()
        {
            // Arrange
            var region = new DBRegion(10, 1024);
            // Act & Assert
            Assert.Throws<ArgumentException>(() => region.Read(99, 0, 1));
        }
        [Fact]
        public void Read_OutOfBounds_ThrowsArgumentOutOfRange()
        {
            // Arrange
            var region = new DBRegion(10, 100);
            // Act & Assert
            Assert.Throws<ArgumentOutOfRangeException>(() => region.Read(1, 0, 101));
        }
        [Fact]
        public void Write_MultipleDBs_StoresSeparately()
        {
            // Arrange
            var region = new DBRegion(10, 100);
            var data1 = new byte[] { 0x01, 0x02 };
            var data2 = new byte[] { 0x03, 0x04 };
            // Act
            region.Write(1, 0, data1);
            region.Write(2, 0, data2);
            var result1 = region.Read(1, 0, 2);
            var result2 = region.Read(2, 0, 2);
            // Assert
            Assert.Equal(data1, result1);
            Assert.Equal(data2, result2);
        }
        [Fact]
        public void Clear_AllBlocks_ZerosAllMemory()
        {
            // Arrange
            var region = new DBRegion(5, 100);
            region.Write(1, 0, new byte[] { 0xFF });
            region.Write(2, 0, new byte[] { 0xFF });
            // Act
            region.Clear();
            // Assert
            Assert.Equal(new byte[] { 0x00 }, region.Read(1, 0, 1));
            Assert.Equal(new byte[] { 0x00 }, region.Read(2, 0, 1));
        }
        [Fact]
        public void Read_IntType_ReturnsCorrectValue()
        {
            // Arrange
            var region = new DBRegion(10, 1024);
            region.WriteInt(1, 0, 12345);
            // Act
            var result = region.ReadInt(1, 0);
            // Assert
            Assert.Equal(12345, result);
        }
        [Fact]
        public void Read_DIntType_ReturnsCorrectValue()
        {
            // Arrange
            var region = new DBRegion(10, 1024);
            region.WriteDInt(1, 0, 123456789);
            // Act
            var result = region.ReadDInt(1, 0);
            // Assert
            Assert.Equal(123456789, result);
        }
        [Fact]
        public void Read_RealType_ReturnsCorrectValue()
        {
            // Arrange
            var region = new DBRegion(10, 1024);
            region.WriteReal(1, 0, 3.14159f);
            // Act
            var result = region.ReadReal(1, 0);
            // Assert
            Assert.Equal(3.14159f, result, 4);
        }
        [Fact]
        public void Read_BoolType_ReturnsCorrectValue()
        {
            // Arrange
            var region = new DBRegion(10, 1024);
            region.WriteBool(1, 0, 0, true);
            // Act
            var result = region.ReadBool(1, 0, 0);
            // Assert
            Assert.True(result);
        }
        [Fact]
        public void ReadString_WriteAndReadString_ReturnsOriginalString()
        {
            // Arrange
            var region = new DBRegion(10, 1024);
            var testString = "Hello";
            // Act
            region.WriteString(1, 0, 254, testString); // æœ€å¤§é•¿åº¦254
            var result = region.ReadString(1, 0, 254);
            // Assert
            Assert.Equal(testString, result);
        }
    }
}
```
- [ ] **Step 2: è¿è¡Œæµ‹è¯•验证失败**
```bash
dotnet test Memory/DBRegionTests.cs -v n
```
预期输出: æµ‹è¯•失败
- [ ] **Step 3: å®žçްDBRegionç±»**
```csharp
using System;
using System.Collections.Generic;
using System.Text;
using WIDESEAWCS_S7Simulator.Core.Interfaces;
namespace WIDESEAWCS_S7Simulator.Core.Memory
{
    /// <summary>
    /// DB区(数据块)实现
    /// </summary>
    public class DBRegion : MemoryRegion, IMemoryRegion
    {
        /// <summary>
        /// DB块字典(块号 -> æ•°æ®ï¼‰
        /// </summary>
        private readonly Dictionary<ushort, byte[]> _blocks;
        /// <summary>
        /// DB块数量
        /// </summary>
        private readonly ushort _blockCount;
        /// <summary>
        /// æ¯ä¸ªDB块大小
        /// </summary>
        private readonly int _blockSize;
        /// <summary>
        /// åŒºåŸŸç±»åž‹
        /// </summary>
        public override string RegionType => "DB";
        /// <summary>
        /// æž„造函数
        /// </summary>
        /// <param name="blockCount">DB块数量</param>
        /// <param name="blockSize">每个DB块大小</param>
        public DBRegion(ushort blockCount, int blockSize) : base(blockCount * blockSize)
        {
            _blockCount = blockCount;
            _blockSize = blockSize;
            _blocks = new Dictionary<ushort, byte[]>();
            // é¢„分配所有DB块
            for (ushort i = 1; i <= blockCount; i++)
            {
                _blocks[i] = new byte[blockSize];
            }
        }
        /// <summary>
        /// è¯»å–指定DB块的数据
        /// </summary>
        /// <param name="dbNumber">DB块号</param>
        /// <param name="offset">块内偏移</param>
        /// <param name="length">长度</param>
        /// <returns>字节数组</returns>
        public byte[] Read(ushort dbNumber, ushort offset, ushort length)
        {
            _lock.EnterReadLock();
            try
            {
                if (!_blocks.ContainsKey(dbNumber))
                    throw new ArgumentException($"DB块不存在: DB{dbNumber}");
                if (offset + length > _blockSize)
                    throw new ArgumentOutOfRangeException(
                        $"读取超出DB{dbNumber}范围: offset={offset}, length={length}, size={_blockSize}");
                byte[] block = _blocks[dbNumber];
                byte[] result = new byte[length];
                Array.Copy(block, offset, result, 0, length);
                return result;
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }
        /// <summary>
        /// å†™å…¥æŒ‡å®šDB块的数据
        /// </summary>
        /// <param name="dbNumber">DB块号</param>
        /// <param name="offset">块内偏移</param>
        /// <param name="data">数据</param>
        public void Write(ushort dbNumber, ushort offset, byte[] data)
        {
            _lock.EnterWriteLock();
            try
            {
                if (!_blocks.ContainsKey(dbNumber))
                    throw new ArgumentException($"DB块不存在: DB{dbNumber}");
                if (offset + data.Length > _blockSize)
                    throw new ArgumentOutOfRangeException(
                        $"写入超出DB{dbNumber}范围: offset={offset}, length={data.Length}, size={_blockSize}");
                byte[] block = _blocks[dbNumber];
                Array.Copy(data, 0, block, offset, data.Length);
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }
        /// <summary>
        /// è¯»å–数据(默认DB1)
        /// </summary>
        public override byte[] Read(ushort offset, ushort length)
        {
            return Read(1, offset, length);
        }
        /// <summary>
        /// å†™å…¥æ•°æ®ï¼ˆé»˜è®¤DB1)
        /// </summary>
        public override void Write(ushort offset, byte[] data)
        {
            Write(1, offset, data);
        }
        /// <summary>
        /// æ¸…空所有DB块
        /// </summary>
        public override void Clear()
        {
            _lock.EnterWriteLock();
            try
            {
                foreach (var block in _blocks.Values)
                {
                    Array.Clear(block, 0, block.Length);
                }
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }
        /// <summary>
        /// è¯»å–位
        /// </summary>
        public bool ReadBool(ushort dbNumber, ushort byteOffset, byte bitOffset)
        {
            if (bitOffset > 7)
                throw new ArgumentOutOfRangeException(nameof(bitOffset), "位偏移必须在0-7之间");
            var data = Read(dbNumber, byteOffset, 1);
            return (data[0] & (1 << bitOffset)) != 0;
        }
        /// <summary>
        /// å†™å…¥ä½
        /// </summary>
        public void WriteBool(ushort dbNumber, ushort byteOffset, byte bitOffset, bool value)
        {
            if (bitOffset > 7)
                throw new ArgumentOutOfRangeException(nameof(bitOffset), "位偏移必须在0-7之间");
            var data = Read(dbNumber, byteOffset, 1);
            if (value)
                data[0] |= (byte)(1 << bitOffset);
            else
                data[0] &= (byte)~(1 << bitOffset);
            Write(dbNumber, byteOffset, data);
        }
        /// <summary>
        /// è¯»å–整数(Int,2字节)
        /// </summary>
        public short ReadInt(ushort dbNumber, ushort offset)
        {
            var data = Read(dbNumber, offset, 2);
            return (short)((data[0] << 8) | data[1]);
        }
        /// <summary>
        /// å†™å…¥æ•´æ•°ï¼ˆInt,2字节)
        /// </summary>
        public void WriteInt(ushort dbNumber, ushort offset, short value)
        {
            var data = new byte[] { (byte)(value >> 8), (byte)(value & 0xFF) };
            Write(dbNumber, offset, data);
        }
        /// <summary>
        /// è¯»å–双整数(DInt,4字节)
        /// </summary>
        public int ReadDInt(ushort dbNumber, ushort offset)
        {
            var data = Read(dbNumber, offset, 4);
            return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
        }
        /// <summary>
        /// å†™å…¥åŒæ•´æ•°ï¼ˆDInt,4字节)
        /// </summary>
        public void WriteDInt(ushort dbNumber, ushort offset, int value)
        {
            var data = new byte[] {
                (byte)(value >> 24),
                (byte)((value >> 16) & 0xFF),
                (byte)((value >> 8) & 0xFF),
                (byte)(value & 0xFF)
            };
            Write(dbNumber, offset, data);
        }
        /// <summary>
        /// è¯»å–浮点数(Real,4字节)
        /// </summary>
        public float ReadReal(ushort dbNumber, ushort offset)
        {
            var bytes = Read(dbNumber, offset, 4);
            return BitConverter.ToSingle(bytes, 0);
        }
        /// <summary>
        /// å†™å…¥æµ®ç‚¹æ•°ï¼ˆReal,4字节)
        /// </summary>
        public void WriteReal(ushort dbNumber, ushort offset, float value)
        {
            var bytes = BitConverter.GetBytes(value);
            Write(dbNumber, offset, bytes);
        }
        /// <summary>
        /// è¯»å–字符串(西门子S7字符串格式:首字节是最大长度,第二字节是实际长度)
        /// </summary>
        public string ReadString(ushort dbNumber, ushort offset, byte maxLength)
        {
            var data = Read(dbNumber, offset, (ushort)(maxLength + 2));
            byte actualLength = data[1];
            if (actualLength > maxLength)
                actualLength = maxLength;
            return Encoding.ASCII.GetString(data, 2, actualLength);
        }
        /// <summary>
        /// å†™å…¥å­—符串(西门子S7字符串格式)
        /// </summary>
        public void WriteString(ushort dbNumber, ushort offset, byte maxLength, string value)
        {
            if (string.IsNullOrEmpty(value))
                value = string.Empty;
            byte[] bytes = Encoding.ASCII.GetBytes(value);
            byte length = (byte)Math.Min(bytes.Length, maxLength);
            byte[] data = new byte[maxLength + 2];
            data[0] = maxLength;
            data[1] = length;
            Array.Copy(bytes, 0, data, 2, length);
            Write(dbNumber, offset, data);
        }
        /// <summary>
        /// é‡Šæ”¾èµ„源
        /// </summary>
        public override void Dispose()
        {
            _blocks.Clear();
            base.Dispose();
        }
    }
}
```
- [ ] **Step 4: è¿è¡Œæµ‹è¯•验证通过**
```bash
dotnet test Memory/DBRegionTests.cs -v n
```
预期输出: æ‰€æœ‰æµ‹è¯•通过
- [ ] **Step 5: æäº¤DB区实现**
```bash
git add .
git commit -m "feat: implement DB region (Data Blocks)
- Add DBRegion class supporting multiple data blocks
- Support bool/int/dint/real/string data types
- Add comprehensive unit tests for all data types
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
### Task 7: å®žçްI/Q/T/C区
**Files:**
- Create: `WIDESEAWCS_S7Simulator.Core/Memory/IRegion.cs`
- Create: `WIDESEAWCS_S7Simulator.Core/Memory/QRegion.cs`
- Create: `WIDESEAWCS_S7Simulator.Core/Memory/TRegion.cs`
- Create: `WIDESEAWCS_S7Simulator.Core/Memory/CRegion.cs`
- [ ] **Step 1: å®žçްI区(输入区)**
```csharp
namespace WIDESEAWCS_S7Simulator.Core.Memory
{
    /// <summary>
    /// I区(输入区/Input)实现
    /// </summary>
    public class IRegion : MemoryRegion
    {
        /// <summary>
        /// åŒºåŸŸç±»åž‹
        /// </summary>
        public override string RegionType => "I";
        /// <summary>
        /// æž„造函数
        /// </summary>
        /// <param name="size">区域大小(字节)</param>
        public IRegion(int size) : base(size)
        {
        }
        /// <summary>
        /// è¯»å–位
        /// </summary>
        public bool ReadBit(ushort byteOffset, byte bitOffset)
        {
            if (bitOffset > 7)
                throw new ArgumentOutOfRangeException(nameof(bitOffset), "位偏移必须在0-7之间");
            _lock.EnterReadLock();
            try
            {
                if (byteOffset >= Size)
                    throw new ArgumentOutOfRangeException(nameof(byteOffset));
                return (_memory[byteOffset] & (1 << bitOffset)) != 0;
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }
        /// <summary>
        /// å†™å…¥ä½
        /// </summary>
        public void WriteBit(ushort byteOffset, byte bitOffset, bool value)
        {
            if (bitOffset > 7)
                throw new ArgumentOutOfRangeException(nameof(bitOffset), "位偏移必须在0-7之间");
            _lock.EnterWriteLock();
            try
            {
                if (byteOffset >= Size)
                    throw new ArgumentOutOfRangeException(nameof(byteOffset));
                if (value)
                    _memory[byteOffset] |= (byte)(1 << bitOffset);
                else
                    _memory[byteOffset] &= (byte)~(1 << bitOffset);
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }
    }
}
```
- [ ] **Step 2: å®žçްQ区(输出区)**
```csharp
namespace WIDESEAWCS_S7Simulator.Core.Memory
{
    /// <summary>
    /// Q区(输出区/Output)实现
    /// </summary>
    public class QRegion : MemoryRegion
    {
        /// <summary>
        /// åŒºåŸŸç±»åž‹
        /// </summary>
        public override string RegionType => "Q";
        /// <summary>
        /// æž„造函数
        /// </summary>
        /// <param name="size">区域大小(字节)</param>
        public QRegion(int size) : base(size)
        {
        }
        /// <summary>
        /// è¯»å–位
        /// </summary>
        public bool ReadBit(ushort byteOffset, byte bitOffset)
        {
            if (bitOffset > 7)
                throw new ArgumentOutOfRangeException(nameof(bitOffset), "位偏移必须在0-7之间");
            _lock.EnterReadLock();
            try
            {
                if (byteOffset >= Size)
                    throw new ArgumentOutOfRangeException(nameof(byteOffset));
                return (_memory[byteOffset] & (1 << bitOffset)) != 0;
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }
        /// <summary>
        /// å†™å…¥ä½
        /// </summary>
        public void WriteBit(ushort byteOffset, byte bitOffset, bool value)
        {
            if (bitOffset > 7)
                throw new ArgumentOutOfRangeException(nameof(bitOffset), "位偏移必须在0-7之间");
            _lock.EnterWriteLock();
            try
            {
                if (byteOffset >= Size)
                    throw new ArgumentOutOfRangeException(nameof(byteOffset));
                if (value)
                    _memory[byteOffset] |= (byte)(1 << bitOffset);
                else
                    _memory[byteOffset] &= (byte)~(1 << bitOffset);
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }
    }
}
```
- [ ] **Step 3: å®žçްT区(定时器区)**
```csharp
namespace WIDESEAWCS_S7Simulator.Core.Memory
{
    /// <summary>
    /// T区(定时器区/Timer)实现
    /// </summary>
    public class TRegion : MemoryRegion
    {
        /// <summary>
        /// åŒºåŸŸç±»åž‹
        /// </summary>
        public override string RegionType => "T";
        /// <summary>
        /// æ¯ä¸ªå®šæ—¶å™¨å ç”¨çš„字节数(S7定时器为2字节)
        /// </summary>
        private const int TimerSize = 2;
        /// <summary>
        /// æž„造函数
        /// </summary>
        /// <param name="timerCount">定时器数量</param>
        public TRegion(int timerCount) : base(timerCount * TimerSize)
        {
        }
        /// <summary>
        /// è¯»å–定时器值
        /// </summary>
        /// <param name="timerNumber">定时器号</param>
        /// <returns>定时器值(毫秒)</returns>
        public ushort ReadTimer(ushort timerNumber)
        {
            ushort offset = (ushort)((timerNumber - 1) * TimerSize);
            var data = Read(offset, TimerSize);
            return (ushort)((data[0] << 8) | data[1]);
        }
        /// <summary>
        /// å†™å…¥å®šæ—¶å™¨å€¼
        /// </summary>
        /// <param name="timerNumber">定时器号</param>
        /// <param name="value">定时器值(毫秒)</param>
        public void WriteTimer(ushort timerNumber, ushort value)
        {
            ushort offset = (ushort)((timerNumber - 1) * TimerSize);
            var data = new byte[] { (byte)(value >> 8), (byte)(value & 0xFF) };
            Write(offset, data);
        }
    }
}
```
- [ ] **Step 4: å®žçްC区(计数器区)**
```csharp
namespace WIDESEAWCS_S7Simulator.Core.Memory
{
    /// <summary>
    /// C区(计数器区/Counter)实现
    /// </summary>
    public class CRegion : MemoryRegion
    {
        /// <summary>
        /// åŒºåŸŸç±»åž‹
        /// </summary>
        public override string RegionType => "C";
        /// <summary>
        /// æ¯ä¸ªè®¡æ•°å™¨å ç”¨çš„字节数(S7计数器为2字节)
        /// </summary>
        private const int CounterSize = 2;
        /// <summary>
        /// æž„造函数
        /// </summary>
        /// <param name="counterCount">计数器数量</param>
        public CRegion(int counterCount) : base(counterCount * CounterSize)
        {
        }
        /// <summary>
        /// è¯»å–计数器值
        /// </summary>
        /// <param name="counterNumber">计数器号</param>
        /// <returns>计数器值</returns>
        public ushort ReadCounter(ushort counterNumber)
        {
            ushort offset = (ushort)((counterNumber - 1) * CounterSize);
            var data = Read(offset, CounterSize);
            return (ushort)((data[0] << 8) | data[1]);
        }
        /// <summary>
        /// å†™å…¥è®¡æ•°å™¨å€¼
        /// </summary>
        /// <param name="counterNumber">计数器号</param>
        /// <param name="value">计数器值</param>
        public void WriteCounter(ushort counterNumber, ushort value)
        {
            ushort offset = (ushort)((counterNumber - 1) * CounterSize);
            var data = new byte[] { (byte)(value >> 8), (byte)(value & 0xFF) };
            Write(offset, data);
        }
    }
}
```
- [ ] **Step 5: éªŒè¯æž„建**
```bash
cd WIDESEAWCS_S7Simulator.Core
dotnet build
```
- [ ] **Step 6: æäº¤I/Q/T/C区实现**
```bash
git add .
git commit -m "feat: implement I/Q/T/C regions
- Add IRegion (Input) with bit operations
- Add QRegion (Output) with bit operations
- Add TRegion (Timer) with timer read/write
- Add CRegion (Counter) with counter read/write
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
### Task 8: å®žçŽ°å†…å­˜å­˜å‚¨
**Files:**
- Create: `WIDESEAWCS_S7Simulator.Core/Interfaces/IMemoryStore.cs`
- Create: `WIDESEAWCS_S7Simulator.Core/Memory/MemoryStore.cs`
- Test: `WIDESEAWCS_S7Simulator.UnitTests/Memory/MemoryStoreTests.cs`
- [ ] **Step 1: åˆ›å»ºå†…存存储接口**
```csharp
namespace WIDESEAWCS_S7Simulator.Core.Interfaces
{
    /// <summary>
    /// å†…存存储接口
    /// </summary>
    public interface IMemoryStore
    {
        /// <summary>
        /// è¯»å–字节数据
        /// </summary>
        /// <param name="address">地址(如 "M100", "DB1.DBD0")</param>
        /// <param name="length">长度</param>
        /// <returns>字节数组</returns>
        byte[] ReadBytes(string address, ushort length);
        /// <summary>
        /// è¯»å–指定类型数据
        /// </summary>
        T Read<T>(string address) where T : struct;
        /// <summary>
        /// å†™å…¥å­—节数据
        /// </summary>
        /// <param name="address">地址</param>
        /// <param name="data">数据</param>
        void WriteBytes(string address, byte[] data);
        /// <summary>
        /// å†™å…¥æŒ‡å®šç±»åž‹æ•°æ®
        /// </summary>
        void Write<T>(string address, T value) where T : struct;
        /// <summary>
        /// èŽ·å–å†…å­˜åŒºåŸŸ
        /// </summary>
        IMemoryRegion GetRegion(string regionType);
        /// <summary>
        /// æ¸…空所有内存
        /// </summary>
        void Clear();
        /// <summary>
        /// å¯¼å‡ºå†…存数据
        /// </summary>
        Dictionary<string, byte[]> Export();
        /// <summary>
        /// å¯¼å…¥å†…存数据
        /// </summary>
        void Import(Dictionary<string, byte[]> data);
    }
}
```
- [ ] **Step 2: ç¼–写内存存储测试**
```csharp
using Xunit;
using WIDESEAWCS_S7Simulator.Core.Memory;
using WIDESEAWCS_S7Simulator.Core.Entities;
namespace WIDESEAWCS_S7Simulator.UnitTests.Memory
{
    public class MemoryStoreTests
    {
        [Fact]
        public void Constructor_WithConfig_CreatesAllRegions()
        {
            // Arrange
            var config = new MemoryRegionConfig
            {
                MRegionSize = 1024,
                DBBlockCount = 10,
                DBBlockSize = 1024,
                IRegionSize = 256,
                QRegionSize = 256,
                TRegionCount = 32,
                CRegionCount = 32
            };
            // Act
            var store = new MemoryStore(config);
            // Assert
            Assert.NotNull(store.GetRegion("M"));
            Assert.NotNull(store.GetRegion("DB"));
            Assert.NotNull(store.GetRegion("I"));
            Assert.NotNull(store.GetRegion("Q"));
            Assert.NotNull(store.GetRegion("T"));
            Assert.NotNull(store.GetRegion("C"));
        }
        [Fact]
        public void WriteBytes_MRegion_WritesToMRegion()
        {
            // Arrange
            var store = new MemoryStore(new MemoryRegionConfig());
            var data = new byte[] { 0x12, 0x34 };
            // Act
            store.WriteBytes("M100", data);
            var result = store.ReadBytes("M100", 2);
            // Assert
            Assert.Equal(data, result);
        }
        [Fact]
        public void Write_ReadInt_ReturnsCorrectValue()
        {
            // Arrange
            var store = new MemoryStore(new MemoryRegionConfig());
            // Act
            store.Write(100, 12345);
            var result = store.Read<int>("M100");
            // Assert
            Assert.Equal(12345, result);
        }
        [Fact]
        public void Write_ReadDInt_ReturnsCorrectValue()
        {
            // Arrange
            var store = new MemoryStore(new MemoryRegionConfig());
            // Act
            store.Write("M100", 123456789);
            var result = store.Read<int>("M100");
            // Assert
            Assert.Equal(123456789, result);
        }
        [Fact]
        public void Write_ReadReal_ReturnsCorrectValue()
        {
            // Arrange
            var store = new MemoryStore(new MemoryRegionConfig());
            // Act
            store.Write("M100", 3.14159f);
            var result = store.Read<float>("M100");
            // Assert
            Assert.Equal(3.14159f, result, 4);
        }
        [Fact]
        public void WriteBytes_DBRegion_WritesToSpecificDB()
        {
            // Arrange
            var store = new MemoryStore(new MemoryRegionConfig());
            var data = new byte[] { 0xAA, 0xBB };
            // Act
            store.WriteBytes("DB1.DBD0", data);
            var result = store.ReadBytes("DB1.DBD0", 2);
            // Assert
            Assert.Equal(data, result);
        }
        [Fact]
        public void Write_IRegion_WritesToIRegion()
        {
            // Arrange
            var store = new MemoryStore(new MemoryRegionConfig());
            // Act
            store.Write("I0", true);
            var result = store.Read<bool>("I0");
            // Assert
            Assert.True(result);
        }
        [Fact]
        public void Clear_AllRegions_ZerosAllMemory()
        {
            // Arrange
            var store = new MemoryStore(new MemoryRegionConfig());
            store.Write("M100", 12345);
            // Act
            store.Clear();
            var result = store.Read<int>("M100");
            // Assert
            Assert.Equal(0, result);
        }
        [Fact]
        public void ExportImport_PreservesAllMemory()
        {
            // Arrange
            var store = new MemoryStore(new MemoryRegionConfig());
            store.Write("M100", 12345);
            store.Write("DB1.DBD0", 67890);
            // Act
            var exported = store.Export();
            var newStore = new MemoryStore(new MemoryRegionConfig());
            newStore.Import(exported);
            // Assert
            Assert.Equal(12345, newStore.Read<int>("M100"));
            Assert.Equal(67890, newStore.Read<int>("DB1.DBD0"));
        }
    }
}
```
- [ ] **Step 3: è¿è¡Œæµ‹è¯•验证失败**
```bash
dotnet test Memory/MemoryStoreTests.cs -v n
```
预期输出: æµ‹è¯•失败
- [ ] **Step 4: å®žçްMemoryStoreç±»**
```csharp
using System.Collections.Generic;
using System.Text.RegularExpressions;
using WIDESEAWCS_S7Simulator.Core.Entities;
using WIDESEAWCS_S7Simulator.Core.Interfaces;
namespace WIDESEAWCS_S7Simulator.Core.Memory
{
    /// <summary>
    /// å†…存存储实现
    /// </summary>
    public class MemoryStore : IMemoryStore
    {
        /// <summary>
        /// å†…存区域字典
        /// </summary>
        private readonly Dictionary<string, IMemoryRegion> _regions;
        /// <summary>
        /// å†…存配置
        /// </summary>
        private readonly MemoryRegionConfig _config;
        /// <summary>
        /// åœ°å€è§£æžæ­£åˆ™è¡¨è¾¾å¼
        /// </summary>
        private static readonly Regex AddressRegex = new Regex(
            @"^(M|DB(\d+)\.)?(I|Q|T|C)?(\d+)(?:\.([BWD]))?$",
            RegexOptions.IgnoreCase | RegexOptions.Compiled);
        /// <summary>
        /// æž„造函数
        /// </summary>
        /// <param name="config">内存配置</param>
        public MemoryStore(MemoryRegionConfig config)
        {
            _config = config;
            _regions = new Dictionary<string, IMemoryRegion>();
            InitializeRegions();
        }
        /// <summary>
        /// åˆå§‹åŒ–所有内存区域
        /// </summary>
        private void InitializeRegions()
        {
            _regions["M"] = new MRegion(_config.MRegionSize);
            _regions["DB"] = new DBRegion(
                (ushort)_config.DBBlockCount,
                _config.DBBlockSize);
            _regions["I"] = new IRegion(_config.IRegionSize);
            _regions["Q"] = new QRegion(_config.QRegionSize);
            _regions["T"] = new TRegion(_config.TRegionCount);
            _regions["C"] = new CRegion(_config.CRegionCount);
        }
        /// <summary>
        /// èŽ·å–å†…å­˜åŒºåŸŸ
        /// </summary>
        public IMemoryRegion GetRegion(string regionType)
        {
            return _regions.TryGetValue(regionType.ToUpper(), out var region)
                ? region
                : throw new KeyNotFoundException($"内存区域不存在: {regionType}");
        }
        /// <summary>
        /// è¯»å–字节数据
        /// </summary>
        public byte[] ReadBytes(string address, ushort length)
        {
            var (regionType, offset, dbNumber) = ParseAddress(address);
            var region = GetRegion(regionType);
            if (regionType == "DB" && dbNumber.HasValue)
            {
                return ((DBRegion)region).Read(dbNumber.Value, offset, length);
            }
            return region.Read(offset, length);
        }
        /// <summary>
        /// è¯»å–指定类型数据
        /// </summary>
        public T Read<T>(string address) where T : struct
        {
            var type = typeof(T);
            var (regionType, offset, dbNumber) = ParseAddress(address);
            var region = GetRegion(regionType);
            if (type == typeof(bool))
            {
                byte bitOffset = (byte)(offset % 8);
                ushort byteOffset = (ushort)(offset / 8);
                if (regionType == "M")
                    return (T)(object)((MRegion)region).ReadBit(byteOffset, bitOffset);
                else if (regionType == "I")
                    return (T)(object)((IRegion)region).ReadBit(byteOffset, bitOffset);
                else if (regionType == "Q")
                    return (T)(object)((QRegion)region).ReadBit(byteOffset, bitOffset);
                else if (regionType == "DB" && dbNumber.HasValue)
                    return (T)(object)((DBRegion)region).ReadBool(dbNumber.Value, byteOffset, bitOffset);
            }
            else if (type == typeof(short) || type == typeof(int))
            {
                if (regionType == "M")
                    return (T)(object)((MRegion)region).ReadInt(offset);
                else if (regionType == "DB" && dbNumber.HasValue)
                    return (T)(object)((DBRegion)region).ReadInt(dbNumber.Value, offset);
            }
            else if (type == typeof(int))
            {
                if (regionType == "M")
                    return (T)(object)((MRegion)region).ReadDInt(offset);
                else if (regionType == "DB" && dbNumber.HasValue)
                    return (T)(object)((DBRegion)region).ReadDInt(dbNumber.Value, offset);
            }
            else if (type == typeof(float))
            {
                if (regionType == "M")
                    return (T)(object)((MRegion)region).ReadReal(offset);
                else if (regionType == "DB" && dbNumber.HasValue)
                    return (T)(object)((DBRegion)region).ReadReal(dbNumber.Value, offset);
            }
            // é»˜è®¤è¯»å–字节
            var bytes = ReadBytes(address, 2);
            if (type == typeof(short))
                return (T)(object)((short)((bytes[0] << 8) | bytes[1]));
            throw new NotSupportedException($"不支持的类型: {type.Name}");
        }
        /// <summary>
        /// å†™å…¥å­—节数据
        /// </summary>
        public void WriteBytes(string address, byte[] data)
        {
            var (regionType, offset, dbNumber) = ParseAddress(address);
            var region = GetRegion(regionType);
            if (regionType == "DB" && dbNumber.HasValue)
            {
                ((DBRegion)region).Write(dbNumber.Value, offset, data);
            }
            else
            {
                region.Write(offset, data);
            }
        }
        /// <summary>
        /// å†™å…¥æŒ‡å®šç±»åž‹æ•°æ®
        /// </summary>
        public void Write<T>(string address, T value) where T : struct
        {
            var type = typeof(T);
            var (regionType, offset, dbNumber) = ParseAddress(address);
            var region = GetRegion(regionType);
            if (type == typeof(bool))
            {
                byte bitOffset = (byte)(offset % 8);
                ushort byteOffset = (ushort)(offset / 8);
                bool boolValue = (bool)(object)value;
                if (regionType == "M")
                    ((MRegion)region).WriteBit(byteOffset, bitOffset, boolValue);
                else if (regionType == "I")
                    ((IRegion)region).WriteBit(byteOffset, bitOffset, boolValue);
                else if (regionType == "Q")
                    ((QRegion)region).WriteBit(byteOffset, bitOffset, boolValue);
                else if (regionType == "DB" && dbNumber.HasValue)
                    ((DBRegion)region).WriteBool(dbNumber.Value, byteOffset, bitOffset, boolValue);
            }
            else if (type == typeof(short))
            {
                short shortValue = (short)(object)value;
                if (regionType == "M")
                    ((MRegion)region).WriteInt(offset, shortValue);
                else if (regionType == "DB" && dbNumber.HasValue)
                    ((DBRegion)region).WriteInt(dbNumber.Value, offset, shortValue);
            }
            else if (type == typeof(int))
            {
                int intValue = (int)(object)value;
                if (regionType == "M")
                    ((MRegion)region).WriteDInt(offset, intValue);
                else if (regionType == "DB" && dbNumber.HasValue)
                    ((DBRegion)region).WriteDInt(dbNumber.Value, offset, intValue);
            }
            else if (type == typeof(float))
            {
                float floatValue = (float)(object)value;
                if (regionType == "M")
                    ((MRegion)region).WriteReal(offset, floatValue);
                else if (regionType == "DB" && dbNumber.HasValue)
                    ((DBRegion)region).WriteReal(dbNumber.Value, offset, floatValue);
            }
            else
            {
                throw new NotSupportedException($"不支持的类型: {type.Name}");
            }
        }
        /// <summary>
        /// æ¸…空所有内存
        /// </summary>
        public void Clear()
        {
            foreach (var region in _regions.Values)
            {
                region.Clear();
            }
        }
        /// <summary>
        /// å¯¼å‡ºå†…存数据
        /// </summary>
        public Dictionary<string, byte[]> Export()
        {
            var data = new Dictionary<string, byte[]>();
            foreach (var kvp in _regions)
            {
                var region = kvp.Value;
                var regionData = region.Read(0, (ushort)region.Size);
                data[$"{kvp.Key}_data"] = regionData;
            }
            return data;
        }
        /// <summary>
        /// å¯¼å…¥å†…存数据
        /// </summary>
        public void Import(Dictionary<string, byte[]> data)
        {
            foreach (var kvp in data)
            {
                if (kvp.Key.EndsWith("_data"))
                {
                    var regionType = kvp.Key.Replace("_data", "");
                    if (_regions.ContainsKey(regionType))
                    {
                        _regions[regionType].Write(0, kvp.Value);
                    }
                }
            }
        }
        /// <summary>
        /// è§£æžåœ°å€
        /// </summary>
        /// <param name="address">地址字符串</param>
        /// <returns>(区域类型, åç§»é‡, DB块号)</returns>
        private (string regionType, ushort offset, ushort? dbNumber) ParseAddress(string address)
        {
            address = address.ToUpper().Trim();
            // è§£æž M100
            if (address.StartsWith("M"))
            {
                var offset = ushort.Parse(address.Substring(1));
                return ("M", offset, null);
            }
            // è§£æž I0, Q0, T1, C1
            if (address.StartsWith("I"))
                return ("I", ushort.Parse(address.Substring(1)), null);
            if (address.StartsWith("Q"))
                return ("Q", ushort.Parse(address.Substring(1)), null);
            if (address.StartsWith("T"))
                return ("T", ushort.Parse(address.Substring(1)), null);
            if (address.StartsWith("C"))
                return ("C", ushort.Parse(address.Substring(1)), null);
            // è§£æž DB1.DBD0 æˆ– DB1.DBW0
            if (address.StartsWith("DB"))
            {
                var parts = address.Substring(2).Split('.');
                var dbNumber = ushort.Parse(parts[0]);
                var offsetPart = parts[1];
                // DBD = DWord (4字节), DBW = Word (2字节), DBB = Byte (1字节)
                ushort offset;
                if (offsetPart.StartsWith("DBD"))
                    offset = ushort.Parse(offsetPart.Substring(3));
                else if (offsetPart.StartsWith("DBW"))
                    offset = ushort.Parse(offsetPart.Substring(3));
                else if (offsetPart.StartsWith("DBB"))
                    offset = ushort.Parse(offsetPart.Substring(3));
                else if (offsetPart.StartsWith("DBX"))
                {
                    // DBX = Bit (位地址)
                    var bitAddress = offsetPart.Substring(3).Split('.');
                    offset = ushort.Parse(bitAddress[0]);
                    // è½¬æ¢ä¸ºå­—节偏移+位偏移
                    offset = (ushort)(offset * 8 + byte.Parse(bitAddress[1]));
                }
                else
                    offset = ushort.Parse(offsetPart);
                return ("DB", offset, dbNumber);
            }
            throw new ArgumentException($"无效的地址格式: {address}");
        }
        /// <summary>
        /// é‡Šæ”¾èµ„源
        /// </summary>
        public void Dispose()
        {
            foreach (var region in _regions.Values)
            {
                if (region is IDisposable disposable)
                {
                    disposable.Dispose();
                }
            }
            _regions.Clear();
        }
    }
}
```
- [ ] **Step 5: è¿è¡Œæµ‹è¯•验证通过**
```bash
dotnet test Memory/MemoryStoreTests.cs -v n
```
预期输出: æ‰€æœ‰æµ‹è¯•通过
- [ ] **Step 6: æäº¤å†…存存储实现**
```bash
git add .
git commit -m "feat: implement MemoryStore with address parsing
- Add MemoryStore class managing all memory regions
- Support M/DB/I/Q/T/C address parsing
- Support bool/short/int/float data types
- Add export/import functionality for persistence
- Add comprehensive unit tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
**计划继续中...** ç”±äºŽè®¡åˆ’很长,我将分多个chunk保存。这是Chunk 1-3,涵盖了项目基础设施、核心实体和内存存储实现。
**是否继续执行下一部分(持久化服务和服务器实例)?**