From 94ad631d316da04c46266ddb1fc6e63e6f8f2fae Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期二, 17 三月 2026 17:34:15 +0800
Subject: [PATCH] feat: 同步协议处理、前端交互与业务联调改动

---
 Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/Protocol/Devices/PlcLinkStackerProtocolHandler.cs |  178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 173 insertions(+), 5 deletions(-)

diff --git a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/Protocol/Devices/PlcLinkStackerProtocolHandler.cs b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/Protocol/Devices/PlcLinkStackerProtocolHandler.cs
index 369a046..ef3321b 100644
--- a/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/Protocol/Devices/PlcLinkStackerProtocolHandler.cs
+++ b/Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/Protocol/Devices/PlcLinkStackerProtocolHandler.cs
@@ -1,16 +1,17 @@
-using Microsoft.Extensions.Options;
+锘縰sing Microsoft.Extensions.Options;
 using WIDESEAWCS_S7Simulator.Core.Interfaces;
 using WIDESEAWCS_S7Simulator.Core.Protocol;
 
 namespace WIDESEAWCS_S7Simulator.Application.Protocol;
 
 /// <summary>
-/// 鍖栨垚鍫嗗灈鏈鸿澶囧崗璁鐞嗗櫒銆�
+/// PlcLink 鍫嗗灈鏈鸿澶囧崗璁鐞嗗櫒銆�
 /// </summary>
 public class PlcLinkStackerProtocolHandler : IDeviceProtocolHandler
 {
     private readonly MirrorAckProtocolHandler _mirrorAckHandler;
     private readonly ProtocolMonitoringOptions _options;
+    private const string ProtocolNameStatic = "PlcLinkStackerProtocol";
 
     public PlcLinkStackerProtocolHandler(
         MirrorAckProtocolHandler mirrorAckHandler,
@@ -24,16 +25,18 @@
 
     public bool Process(IMemoryStore memoryStore, ProtocolTemplate template, ProtocolRuntimeState runtimeState)
     {
+        // 鍏堟墽琛� PlcLink 鍫嗗灈鏈哄浐瀹� DB 浜や簰閫昏緫銆�
+        var changed = ApplyPlcLinkStackerOffsets(memoryStore, template, runtimeState);
+
         var ruleIds = _options.PlcLinkStackerRuleIds
             .Where(x => !string.IsNullOrWhiteSpace(x))
             .Distinct(StringComparer.OrdinalIgnoreCase)
             .ToArray();
         if (ruleIds.Length == 0)
         {
-            return false;
+            return changed;
         }
 
-        bool changed = false;
         foreach (var ruleId in ruleIds)
         {
             var rule = _options.MirrorAckRules
@@ -43,9 +46,174 @@
                 continue;
             }
 
-            changed |= _mirrorAckHandler.Process(memoryStore, template, runtimeState, rule, $"{ProtocolName}:{ruleId}");
+            var stateKey = $"{ProtocolName}:{ruleId}";
+            changed |= _mirrorAckHandler.Process(memoryStore, template, runtimeState, rule, stateKey);
         }
 
         return changed;
     }
+
+    /// <summary>
+    /// PlcLink 鍫嗗灈鏈哄亸绉讳氦浜掕鍒欙細
+    /// 1) 鍒濆鍖栵紙浠呴娆★級锛欴B910 offset6=1锛宱ffset20=1
+    /// 2) DB900 offset40=1锛欴B910 offset28=DB900 offset2锛孌B910 offset20=2
+    /// 3) DB900 offset40=2锛欴B910 offset28=0锛孌B910 offset20=1
+    /// </summary>
+    private static bool ApplyPlcLinkStackerOffsets(
+        IMemoryStore memoryStore,
+        ProtocolTemplate template,
+        ProtocolRuntimeState runtimeState)
+    {
+        const int db900 = 900;
+        const int db910 = 910;
+        bool changed = false;
+
+        var db900Offset2 = ResolveField(template, db900, 2);
+        var db900Offset40 = ResolveField(template, db900, 40);
+        var db910Offset6 = ResolveField(template, db910, 6);
+        var db910Offset20 = ResolveField(template, db910, 20);
+        var db910Offset28 = ResolveField(template, db910, 28);
+        var db910Offset42 = ResolveField(template, db910, 42);
+
+        // 鍒濆鍖栧彧鍦ㄥ疄渚嬪惎鍔ㄥ悗鐨勭涓�娆¤疆璇㈠啓鍏ヤ竴娆★紝閬垮厤瑕嗙洊鐜板満鍊笺��
+        var initKey = BuildInitStateKey(template);
+        if (!runtimeState.LastWcsAckByKey.ContainsKey(initKey))
+        {
+            changed |= TryWriteNumeric(memoryStore, db910Offset6, 1);
+            changed |= TryWriteNumeric(memoryStore, db910Offset20, 1);
+            runtimeState.LastWcsAckByKey[initKey] = 1;
+        }
+
+        var offset40 = TryReadNumeric(memoryStore, db900Offset40);
+        if (offset40 == 1)
+        {
+            var offset2 = TryReadNumeric(memoryStore, db900Offset2);
+            changed |= TryWriteNumeric(memoryStore, db910Offset28, offset2);
+            changed |= TryWriteNumeric(memoryStore, db910Offset20, 2);
+        }
+        else if (offset40 == 2)
+        {
+            changed |= TryWriteNumeric(memoryStore, db910Offset28, 0);
+            changed |= TryWriteNumeric(memoryStore, db910Offset20, 1);
+            changed |= TryWriteNumeric(memoryStore, db900Offset40, 0);
+            changed |= TryWriteNumeric(memoryStore, db910Offset42, 0);
+        }
+
+        return changed;
+    }
+
+    private static string BuildInitStateKey(ProtocolTemplate template)
+    {
+        var templateId = string.IsNullOrWhiteSpace(template.Id) ? "unknown" : template.Id;
+        return $"{ProtocolNameStatic}:Init:{templateId}:DB910";
+    }
+
+    /// <summary>
+    /// 浼樺厛鎸夈�孌B鍙�+Offset銆嶅尮閰嶆ā鏉垮瓧娈碉紱鎵句笉鍒板垯鎸� Offset 鍥為��锛涗粛鎵句笉鍒板垯浣跨敤榛樿 Byte 鏄犲皠銆�
+    /// </summary>
+    private static ProtocolFieldMapping ResolveField(ProtocolTemplate template, int dbNumber, int offset)
+    {
+        var byDb = template.Fields.FirstOrDefault(x => x.DbNumber == dbNumber && x.Offset == offset);
+        if (byDb != null)
+        {
+            return byDb;
+        }
+
+        var byOffset = template.Fields.FirstOrDefault(x => x.Offset == offset);
+        if (byOffset != null)
+        {
+            return byOffset;
+        }
+
+        return new ProtocolFieldMapping
+        {
+            DbNumber = dbNumber,
+            Offset = offset,
+            Bit = 0,
+            DataType = ProtocolDataType.Byte
+        };
+    }
+
+    private static string BuildAddress(ProtocolFieldMapping field)
+    {
+        return field.DataType switch
+        {
+            ProtocolDataType.Bool => $"DB{field.DbNumber}.DBX{field.Offset}.{field.Bit}",
+            ProtocolDataType.Int => $"DB{field.DbNumber}.DBW{field.Offset}",
+            ProtocolDataType.DInt => $"DB{field.DbNumber}.DBD{field.Offset}",
+            _ => $"DB{field.DbNumber}.DBB{field.Offset}"
+        };
+    }
+
+    private static int TryReadNumeric(IMemoryStore memoryStore, ProtocolFieldMapping field)
+    {
+        var address = BuildAddress(field);
+        return field.DataType switch
+        {
+            ProtocolDataType.Bool => memoryStore.Read<bool>(address) ? 1 : 0,
+            ProtocolDataType.Int => memoryStore.Read<short>(address),
+            ProtocolDataType.DInt => memoryStore.Read<int>(address),
+            ProtocolDataType.String => TryReadStringNumeric(memoryStore, field),
+            _ => memoryStore.Read<byte>(address)
+        };
+    }
+
+    private static int TryReadStringNumeric(IMemoryStore memoryStore, ProtocolFieldMapping field)
+    {
+        var len = field.Length > 0 ? field.Length : 16;
+        var bytes = memoryStore.ReadBytes(BuildAddress(field), (ushort)len);
+        var text = System.Text.Encoding.ASCII.GetString(bytes).Trim('\0', ' ');
+        return int.TryParse(text, out var value) ? value : 0;
+    }
+
+    private static bool TryWriteNumeric(IMemoryStore memoryStore, ProtocolFieldMapping field, int targetValue)
+    {
+        var address = BuildAddress(field);
+        switch (field.DataType)
+        {
+            case ProtocolDataType.Bool:
+            {
+                var next = targetValue != 0;
+                var current = memoryStore.Read<bool>(address);
+                if (current == next) return false;
+                memoryStore.Write(address, next);
+                return true;
+            }
+            case ProtocolDataType.Int:
+            {
+                var next = (short)targetValue;
+                var current = memoryStore.Read<short>(address);
+                if (current == next) return false;
+                memoryStore.Write(address, next);
+                return true;
+            }
+            case ProtocolDataType.DInt:
+            {
+                var current = memoryStore.Read<int>(address);
+                if (current == targetValue) return false;
+                memoryStore.Write(address, targetValue);
+                return true;
+            }
+            case ProtocolDataType.String:
+            {
+                var len = field.Length > 0 ? field.Length : 16;
+                var nextText = targetValue.ToString();
+                var nextBytes = new byte[len];
+                var raw = System.Text.Encoding.ASCII.GetBytes(nextText);
+                Array.Copy(raw, nextBytes, Math.Min(raw.Length, nextBytes.Length));
+                var currentBytes = memoryStore.ReadBytes(address, (ushort)len);
+                if (currentBytes.SequenceEqual(nextBytes)) return false;
+                memoryStore.WriteBytes(address, nextBytes);
+                return true;
+            }
+            default:
+            {
+                var next = (byte)targetValue;
+                var current = memoryStore.Read<byte>(address);
+                if (current == next) return false;
+                memoryStore.Write(address, next);
+                return true;
+            }
+        }
+    }
 }

--
Gitblit v1.9.3