From adf41ccad7f0abd082d899c390995a0a2e584044 Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期五, 13 三月 2026 12:09:08 +0800
Subject: [PATCH] docs: add S7 PLC simulator implementation plan
---
Code/WCS/WIDESEAWCS_Server/docs/superpowers/plans/2026-03-13-s7-plc-simulator-implementation.md | 2478 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 2,478 insertions(+), 0 deletions(-)
diff --git a/Code/WCS/WIDESEAWCS_Server/docs/superpowers/plans/2026-03-13-s7-plc-simulator-implementation.md b/Code/WCS/WIDESEAWCS_Server/docs/superpowers/plans/2026-03-13-s7-plc-simulator-implementation.md
new file mode 100644
index 0000000..18331bc
--- /dev/null
+++ b/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.
+
+**鐩爣:** 鏋勫缓涓�涓熀浜嶩SL Communication搴撶殑瑗块棬瀛怱7 PLC妯℃嫙鍣ㄧ郴缁燂紝鏀寔澶氬疄渚嬨�乄eb绠$悊鐣岄潰鍜屾暟鎹寔涔呭寲銆�
+
+**鏋舵瀯:** 閲囩敤DDD鍒嗗眰鏋舵瀯锛孋ore灞傚疄鐜伴鍩熼�昏緫锛孉pplication灞傛彁渚涘簲鐢ㄦ湇鍔★紝Server灞傛彁渚沇eb API锛學eb灞傛彁渚涚鐞嗙晫闈€�備娇鐢℉SL Communication搴撳疄鐜癝7鏈嶅姟鍣紝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` | 瀹㈡埛绔繛鎺TO |
+| `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` | 瀹㈡埛绔鐞咥PI |
+| `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: 鍒涘缓瑙e喅鏂规鍜岄」鐩粨鏋�
+
+- [ ] **Step 1: 鍦╓CS鐩綍涓嬪垱寤篠7妯℃嫙鍣ㄨВ鍐虫柟妗�**
+
+```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: 楠岃瘉瑙e喅鏂规鏋勫缓**
+
+```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>
+ /// 瑗块棬瀛怭LC鍨嬪彿
+ /// </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: 鍒犻櫎榛樿鐢熸垚鐨凜lass1.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>
+ /// 瀹㈡埛绔疘P鍦板潃鍜岀鍙�
+ /// </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>
+ /// 鍖哄煙绫诲瀷锛圡/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
+```
+
+棰勬湡杈撳嚭: 娴嬭瘯澶辫触锛圡Region绫讳笉瀛樺湪锛�
+
+- [ ] **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>
+ /// 璇诲彇鍙屽瓧锛圖Word锛�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>
+ /// 鍐欏叆鍙屽瓧锛圖Word锛�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>
+ /// 璇诲彇鏁存暟锛圛nt锛�2瀛楄妭锛屾湁绗﹀彿锛�
+ /// </summary>
+ public short ReadInt(ushort byteOffset)
+ {
+ return (short)ReadWord(byteOffset);
+ }
+
+ /// <summary>
+ /// 鍐欏叆鏁存暟锛圛nt锛�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[]>();
+
+ // 棰勫垎閰嶆墍鏈塂B鍧�
+ 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>
+ /// 璇诲彇鏁版嵁锛堥粯璁B1锛�
+ /// </summary>
+ public override byte[] Read(ushort offset, ushort length)
+ {
+ return Read(1, offset, length);
+ }
+
+ /// <summary>
+ /// 鍐欏叆鏁版嵁锛堥粯璁B1锛�
+ /// </summary>
+ public override void Write(ushort offset, byte[] data)
+ {
+ Write(1, offset, data);
+ }
+
+ /// <summary>
+ /// 娓呯┖鎵�鏈塂B鍧�
+ /// </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>
+ /// 璇诲彇鏁存暟锛圛nt锛�2瀛楄妭锛�
+ /// </summary>
+ public short ReadInt(ushort dbNumber, ushort offset)
+ {
+ var data = Read(dbNumber, offset, 2);
+ return (short)((data[0] << 8) | data[1]);
+ }
+
+ /// <summary>
+ /// 鍐欏叆鏁存暟锛圛nt锛�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>
+ /// 璇诲彇瀛楃涓诧紙瑗块棬瀛怱7瀛楃涓叉牸寮忥細棣栧瓧鑺傛槸鏈�澶ч暱搴︼紝绗簩瀛楄妭鏄疄闄呴暱搴︼級
+ /// </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>
+ /// 鍐欏叆瀛楃涓诧紙瑗块棬瀛怱7瀛楃涓叉牸寮忥級
+ /// </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>
+ /// 鍦板潃瑙f瀽姝e垯琛ㄨ揪寮�
+ /// </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>
+ /// 瑙f瀽鍦板潃
+ /// </summary>
+ /// <param name="address">鍦板潃瀛楃涓�</param>
+ /// <returns>(鍖哄煙绫诲瀷, 鍋忕Щ閲�, DB鍧楀彿)</returns>
+ private (string regionType, ushort offset, ushort? dbNumber) ParseAddress(string address)
+ {
+ address = address.ToUpper().Trim();
+
+ // 瑙f瀽 M100
+ if (address.StartsWith("M"))
+ {
+ var offset = ushort.Parse(address.Substring(1));
+ return ("M", offset, null);
+ }
+
+ // 瑙f瀽 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);
+
+ // 瑙f瀽 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淇濆瓨銆傝繖鏄疌hunk 1-3锛屾兜鐩栦簡椤圭洰鍩虹璁炬柦銆佹牳蹇冨疄浣撳拰鍐呭瓨瀛樺偍瀹炵幇銆�
+
+**鏄惁缁х画鎵ц涓嬩竴閮ㄥ垎锛堟寔涔呭寲鏈嶅姟鍜屾湇鍔″櫒瀹炰緥锛夛紵**
--
Gitblit v1.9.3