wanshenmean
8 天以前 559bb7b4be83575cc5fedee98484647243c96f89
feat: 空托盘出库完整流程 + 输送线调度优化 + S7Simulator 协议同步增强

feat: 空托盘出库完整流程 + 输送线调度优化 + S7Simulator 协议同步增强

【WCS - 空托盘出库完整流程】

- ConfigKey: 新增 OutboundFinishTaskTray 枚举,表示通知 WMS 空托盘出库完成
- ApiRouteCacheWarmupHostedService: 注册 OutboundFinishTaskTray API 路由
- OutboundTaskFlowService:
- 空托盘出库完成时调用 WMS 的 OutboundFinishTaskTray 接口通知完成
- 增加 GetWMSRobotTask 调用失败的错误处理
- RobotTaskService: 修正 RobotSourceAddress/TargetAddress 使用 taskDTO 的
SourceAddress/TargetAddress,替代之前的 lineNo

【WCS - 输送线调度优化】

- CommonConveyorLineNewJob:
- 禁用 Parallel.For 并行处理,改为串行 foreach,避免并发问题
- JsonSerializer 替换为 JsonConvert.DeserializeObject
- CV_State 判断从 .ObjToBool() 改为 == 1
- existingTask 查询改用 Db.Queryable 方式
- 修复 WMS 任务反序列化:WMSTaskDTO 替代 List<WMSTaskDTO>
- ConveyorLineDispatchHandler:
- 出库完成时将 WCS_ACK 确认移至 UpdateTaskStatusToNext 之后,
确保状态更新成功后再回复 PLC

【WMS - 空托盘出库完成接口 + 机械手任务地址规则】

- ILocationInfoService / LocationInfoService: 新增 GetLocationInfoAsync(int id)
- ITaskService: 新增 OutboundFinishTaskTrayAsync 接口声明
- TaskService_WCS:
- GetOutBoundTrayTaskAsync: 新增任务存在性检查(TargetAddress 查重),
有任务时直接返回;创建任务时增加货位锁定和事务包装
- CreateRobotTaskAsync: 从配置 RobotTaskAddressRules 读取源/目标地址,
而非直接使用 lineNo
- TaskController: 注册 OutboundFinishTaskTrayAsync 接口;
修正 CreateTaskInboundTrayAsync 方法注释(入库而非出库)
- appsettings.json:
- 修正 AGV 配置键名(AGV_OutTaskComplete → A0GV_OutTaskComplete)
- 新增 RobotTaskAddressRules 配置节

【S7Simulator - 协议同步增强】

- InstanceSyncService:
- 预计算 DB 编号缓存字典,避免在每个 FieldKey 中重复解析
- MemoryConfig.DBBlockNumbers 改为根据实际使用的 DB 编号动态生成
- 删除已废弃实例的 memory.json(instance-CWSC1/GWSC1/HCSC1/Line1/Line2)
- 新增实例目录:instance-1001、instance-1002、instance-1003、instance-PM
- protocol-templates.json: fieldKey 加上 DeviceChildCode 前缀(与 fieldKey
前缀变更一致,1600+ 行更新)

fix: 协议模板fieldKey加上DeviceChildCode前缀

例如 11001_Target

fix: 修复数据库同步问题 - DbType.SqlServer 和 SugarTable 属性

- DatabaseDeviceService 使用 DbType.SqlServer 显式设置
- DTO 添加 [SugarTable] 属性指定真实表名
- appsettings.json 添加 WcsDb 配置

fix: 将 Initial Catalog 改为 Database 尝试解决 SqlSugar 连接问题

chore: 构建验证通过

feat: Program.cs 注册同步服务并启动时同步

feat: 添加 SyncController API 接口

feat: 添加 InstanceSyncService 同步协调逻辑

feat: 添加 DatabaseDeviceService 读取 WCS 数据库

feat: 添加 WcsDbOptions 配置类

feat: 添加 WCS 设备 DTO

feat: 添加 SqlSugarCore 依赖用于数据库连接

fix: 整合启动同步逻辑,使用环境变量占位符

- 将WCS数据库同步整合到现有的启动Task.Run中
- 连接串密码使用${WCS_DB_PASSWORD}占位符
- 修正spec中DbType为整数类型

fix: 修正协议模板ID格式和数据类型映射

- 协议模板ID改为 protocol-{DeviceCode}(每个设备一个模板)
- 更新数据类型映射表以匹配 ProtocolDataType 枚举实际值
- Direction 改为 Bidirectional (2)

docs: 添加数据库实例同步实现计划

docs: 添加数据库实例同步设计文档
已添加10个文件
已重命名1个文件
已删除5个文件
已修改19个文件
4136 ■■■■■ 文件已修改
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/HttpEnum/ConfigKey.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/HostedService/ApiRouteCacheWarmupHostedService.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/OutboundTaskFlowService.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/RobotTaskService.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_IBasicService/ILocationInfoService.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/DTOs/WcsDeviceDto.cs 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/DatabaseDeviceService.cs 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/InstanceSyncService.cs 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/WIDESEAWCS_S7Simulator.Application.csproj 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/WcsDbOptions.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/SyncController.cs 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-1001/config.json 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-1002/config.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-1003/config.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-CWSC1/config.json 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-CWSC1/memory.json 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-GWSC1/config.json 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-GWSC1/memory.json 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-HCSC1/config.json 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-HCSC1/memory.json 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-Line1/memory.json 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-Line2/config.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-PM/config.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/protocol-templates.json 2714 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Program.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/appsettings.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/docs/superpowers/plans/2026-04-08-database-instance-sync-plan.md 593 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/docs/superpowers/specs/2026-04-08-database-instance-sync-design.md 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/HttpEnum/ConfigKey.cs
@@ -79,6 +79,11 @@
        /// </summary>
        CreateRobotSplitPalletTask,
        /// <summary>
        /// ç©ºæ‰˜ç›˜å‡ºåº“完成(根据任务号和托盘号通知WMS空托盘出库完成)
        /// </summary>
        OutboundFinishTaskTray
        #endregion WMS接口
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/HostedService/ApiRouteCacheWarmupHostedService.cs
@@ -27,7 +27,8 @@
            (nameof(ConfigKey.RelocationFinishTask), "Task/RelocationFinishTask"),
            (nameof(ConfigKey.CreateRobotGroupPalletTask), "Task/CreateRobotGroupPalletTask"),
            (nameof(ConfigKey.CreateRobotChangePalletTask), "Task/CreateRobotChangePalletTask"),
            (nameof(ConfigKey.CreateRobotSplitPalletTask), "Task/CreateRobotSplitPalletTask")
            (nameof(ConfigKey.CreateRobotSplitPalletTask), "Task/CreateRobotSplitPalletTask"),
            (nameof(ConfigKey.OutboundFinishTaskTray), "Task/OutboundFinishTaskTray")
        };
        private readonly ICacheService _cache;
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/OutboundTaskFlowService.cs
@@ -79,7 +79,12 @@
            if (task.TaskStatus == (int)TaskOutStatusEnum.Line_OutFinish && task.TaskType == (int)TaskOutboundTypeEnum.OutEmpty)
            {
                return _robotTaskService.GetWMSRobotTask(task);
                WebResponseContent content = _robotTaskService.GetWMSRobotTask(task);
                if (!content.Status)
                {
                    return content;
                }
                return OutboundFinishTaskTray(task);
            }
            if (task.TaskStatus == (int)TaskOutStatusEnum.Line_OutExecuting)
@@ -183,5 +188,22 @@
            return WebResponseContent.Instance.OK();
        }
        /// <summary>
        /// ç©ºæ‰˜ç›˜å‡ºåº“完成同步到WMS。
        /// </summary>
        /// <param name="task">任务实体。</param>
        /// <returns>同步结果。</returns>
        private WebResponseContent OutboundFinishTaskTray(Dt_Task task)
        {
            var result = _httpClientHelper.Post<WebResponseContent>(
                nameof(ConfigKey.OutboundFinishTaskTray),
                new CreateTaskDto { PalletCode = task.PalletCode, SourceAddress = task.SourceAddress }.ToJson());
            if (!result.IsSuccess || !result.Data.Status)
                return WebResponseContent.Instance.Error($"调用WMS接口更新任务状态失败,任务号:【{task.TaskNum}】,错误信息:【{result.Data?.Message}】");
            return WebResponseContent.Instance.OK();
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/RobotTaskService.cs
@@ -82,8 +82,8 @@
                    RobotSourceAddressLineCode = stockDTO.SourceLineNo,
                    RobotTargetAddressLineCode = stockDTO.TargetLineNo,
                    RobotRoadway = stockDTO.Roadway,
                    RobotSourceAddress = stockDTO.SourceLineNo,
                    RobotTargetAddress = stockDTO.TargetLineNo,
                    RobotSourceAddress = taskDTO.SourceAddress,
                    RobotTargetAddress = taskDTO.TargetAddress,
                    RobotSourceAddressPalletCode = stockDTO.SourcePalletNo,
                    RobotTargetAddressPalletCode = stockDTO.TargetPalletNo,
                    RobotTaskType = taskDTO.TaskType,
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs
@@ -1,6 +1,7 @@
using MapsterMapper;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Quartz;
using SqlSugar;
using System.Text.Json;
@@ -131,19 +132,20 @@
                    }
                    // åˆ›å»ºå¹¶è¡Œé€‰é¡¹ï¼Œé™åˆ¶æœ€å¤§å¹¶å‘æ•°
                    var parallelOptions = new ParallelOptions
                    {
                        // é™åˆ¶å¹¶å‘数:子设备数量和 CPU æ ¸å¿ƒæ•°*2 çš„较小值
                        MaxDegreeOfParallelism = Math.Min(childDeviceCodes.Count, Environment.ProcessorCount * 2),
                    };
                    //var parallelOptions = new ParallelOptions
                    //{
                    //    // é™åˆ¶å¹¶å‘数:子设备数量和 CPU æ ¸å¿ƒæ•°*2 çš„较小值
                    //    MaxDegreeOfParallelism = Math.Min(childDeviceCodes.Count, Environment.ProcessorCount * 2),
                    //};
                    _logger.LogDebug("Execute:开始并行处理输送线 {DeviceCode},子设备数量: {Count}", conveyorLine.DeviceCode, childDeviceCodes.Count);
                    QuartzLogger.Debug($"开始并行处理输送线,子设备数量: {childDeviceCodes.Count}", conveyorLine.DeviceCode);
                    // å¹¶è¡Œå¤„理每个子设备
                    Parallel.For(0, childDeviceCodes.Count, parallelOptions, i =>
                    //Parallel.For(0, childDeviceCodes.Count, parallelOptions, i =>
                    foreach (var childDeviceCode in childDeviceCodes)
                    {
                        string childDeviceCode = childDeviceCodes[i];
                        //string childDeviceCode = childDeviceCodes[i];
                        var correlationId = Guid.NewGuid().ToString("N");
                        try
                        {
@@ -155,7 +157,7 @@
                            {
                                _logger.LogDebug("Execute:子设备 {ChildDeviceCode} å‘½ä»¤ä¸ºç©ºï¼Œè·³è¿‡", childDeviceCode);
                                QuartzLogger.Debug($"子设备 {childDeviceCode} å‘½ä»¤ä¸ºç©ºï¼Œè·³è¿‡", conveyorLine.DeviceCode);
                                return;
                                return Task.CompletedTask;
                            }
                            // å¦‚æžœ WCS_ACK ä¸º 1,先清除(表示处理过上一次请求)
@@ -171,10 +173,10 @@
                            if (checkPalletPositions.Any(x => x.Code == childDeviceCode))
                            {
                                // æ£€æŸ¥è¾“送线状态(是否有托盘)
                                if (command.CV_State.ObjToBool())
                                if (command.CV_State == 1)
                                {
                                    // æ£€æŸ¥è¯¥ä½ç½®æ˜¯å¦å·²æœ‰ä»»åŠ¡
                                    var existingTask = _taskService.Repository.QueryFirst(x => x.TargetAddress == childDeviceCode);
                                    var existingTask = _taskService.Db.Queryable<Dt_Task>().First(x => x.TargetAddress == childDeviceCode);
                                    if (existingTask.IsNullOrEmpty())
                                    {
                                        // æ²¡æœ‰ä»»åŠ¡ï¼Œå‘ WMS è¯·æ±‚出库托盘任务
@@ -191,9 +193,10 @@
                                        // å¦‚果请求成功,接收 WMS è¿”回的任务
                                        if (responseResult.IsSuccess && responseResult.Data.Status)
                                        {
                                            var wmsTask = JsonSerializer.Deserialize<List<WMSTaskDTO>>(responseResult.Data.Data.Serialize());
                                            var wmsTask = JsonConvert.DeserializeObject<WMSTaskDTO>(responseResult.Data.Data.ToString());
                                            List<WMSTaskDTO> taskDTOs = new List<WMSTaskDTO> { wmsTask };
                                            if (wmsTask != null)
                                                _taskService.ReceiveWMSTask(wmsTask);
                                                _taskService.ReceiveWMSTask(taskDTOs);
                                        }
                                    }
                                }
@@ -203,9 +206,9 @@
                            // åªæœ‰å½“ PLC_STB ä¸º 1 æ—¶æ‰å¤„理任务
                            if (command.PLC_STB != 1)
                            {
                                _logger.LogDebug("Execute:子设备 {ChildDeviceCode} PLC_STB ä¸ä¸º1,跳过", childDeviceCode);
                                QuartzLogger.Debug($"子设备 {childDeviceCode} PLC_STB ä¸ä¸º1,跳过", conveyorLine.DeviceCode);
                                return;
                                //_logger.LogDebug("Execute:子设备 {ChildDeviceCode} PLC_STB ä¸ä¸º1,跳过", childDeviceCode);
                                //QuartzLogger.Debug($"子设备 {childDeviceCode} PLC_STB ä¸ä¸º1,跳过", conveyorLine.DeviceCode);
                                return Task.CompletedTask;
                            }
                            // ========== å¤„理无托盘条码的情况 ==========
@@ -215,7 +218,7 @@
                                _logger.LogDebug("Execute:子设备 {ChildDeviceCode} æ— æ‰˜ç›˜æ¡ç ï¼Œè¯·æ±‚出库任务", childDeviceCode);
                                QuartzLogger.Debug($"子设备 {childDeviceCode} æ— æ‰˜ç›˜æ¡ç ï¼Œè¯·æ±‚出库任务", conveyorLine.DeviceCode);
                                _conveyorLineDispatch.RequestOutbound(conveyorLine, command, childDeviceCode);
                                return;
                                return Task.CompletedTask;
                            }
                            // ========== å¤„理已有任务号的情况 ==========
@@ -229,7 +232,7 @@
                                    QuartzLogger.Info($"处理任务 {task.TaskNum},状态: {task.TaskStatus}", conveyorLine.DeviceCode);
                                    // å¤„理任务状态(根据状态分发到不同方法)
                                    ProcessTaskState(conveyorLine, command, task, childDeviceCode);
                                    return;
                                    return Task.CompletedTask;
                                }
                            }
                        }
@@ -239,7 +242,7 @@
                            _logger.LogError(innerEx, "Execute:子设备 {ChildDeviceCode} å¤„理异常,CorrelationId: {CorrelationId}", childDeviceCode, correlationId);
                            QuartzLogger.Error($"子设备处理异常: {innerEx.Message}", conveyorLine.DeviceCode, innerEx);
                        }
                    });
                    }
                }
            }
            catch (Exception ex)
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs
@@ -309,12 +309,12 @@
            Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
            if (task != null)
            {
                // å›žå¤ ACK ç¡®è®¤
                conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode);
                // æ›´æ–°ä»»åŠ¡çŠ¶æ€åˆ°ä¸‹ä¸€é˜¶æ®µï¼ˆé€šå¸¸æ˜¯å®Œæˆï¼‰
                WebResponseContent content = _taskService.UpdateTaskStatusToNext(task);
                // å›žå¤ ACK ç¡®è®¤
                conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode);
                _logger.LogInformation("ConveyorLineOutFinish:出库完成,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode);
                QuartzLogger.Info($"出库完成,任务号: {task.TaskNum}", conveyorLine.DeviceCode);
            }
Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
@@ -173,6 +173,16 @@
        }
        /// <summary>
        /// æ ¹æ®è´§ä½ID获取货位信息
        /// </summary>
        /// <param name="id">货位id</param>
        /// <returns>货位信息</returns>
        public async Task<Dt_LocationInfo> GetLocationInfoAsync(int id)
        {
            return await BaseDal.QueryFirstAsync(x => x.Id == id);
        }
        /// <summary>
        /// æ›´æ–°è´§ä½ä¿¡æ¯
        /// </summary>
        /// <param name="locationInfo">货位信息对象</param>
Code/WMS/WIDESEA_WMSServer/WIDESEA_IBasicService/ILocationInfoService.cs
@@ -88,5 +88,12 @@
        /// <param name="taskNum">任务号</param>
        /// <returns>任务信息</returns>
        Task<WebResponseContent> TransferCheckAsync(int taskNum);
        /// <summary>
        /// æ ¹æ®è´§ä½ID获取货位信息
        /// </summary>
        /// <param name="id">货位id</param>
        /// <returns>货位信息</returns>
        Task<Dt_LocationInfo> GetLocationInfoAsync(int id);
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs
@@ -93,6 +93,11 @@
        Task<WebResponseContent> GetOutBoundTrayTaskAsync(CreateTaskDto taskDto);
        /// <summary>
        /// ç©ºæ‰˜ç›˜å‡ºåº“完成
        /// </summary>
        Task<WebResponseContent> OutboundFinishTaskTrayAsync(CreateTaskDto taskDto);
        /// <summary>
        /// ä¿®æ”¹ä»»åŠ¡çŠ¶æ€ï¼ˆæ ¹æ®ä»»åŠ¡ID修改为指定状态)
        /// </summary>
        /// <param name="taskId"></param>
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs
@@ -426,6 +426,13 @@
        {
            try
            {
                var dt_Task = await BaseDal.QueryFirstAsync(x => x.TargetAddress == taskDto.TargetAddress);
                if (!dt_Task.IsNullOrEmpty())
                {
                    var taskDTO = dt_Task.Adapt<WMSTaskDTO>();
                    return WebResponseContent.Instance.OK("任务创建成功", taskDTO);
                }
                var stockInfo = await _stockInfoService.Repository.QueryDataNavFirstAsync(x => x.LocationDetails.WarehouseId == taskDto.WarehouseId && x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode() && x.StockStatus == StockStatusEmun.空托盘库存.GetHashCode());
                if (stockInfo == null)
                    return WebResponseContent.Instance.Error("未找到对应的库存信息");
@@ -446,12 +453,22 @@
                    TaskNum = await BaseDal.GetTaskNo(),
                    Creater = "system",
                };
                return await ExecuteWithinTransactionAsync(async () =>
                {
                    var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationId);
                    locationInfo.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo);
                    if (!updateLocationResult)
                        return WebResponseContent.Instance.Error("任务创建失败");
                var taskDtos = task.Adapt<WMSTaskDTO>();
                var addResult = await BaseDal.AddDataAsync(task) > 0;
                if (!addResult)
                    return WebResponseContent.Instance.Error("任务创建失败");
                return WebResponseContent.Instance.OK("任务创建成功", taskDtos);
                });
            }
            catch (Exception ex)
            {
@@ -776,6 +793,7 @@
                    if (stockInfo.LocationId > 0 || !string.IsNullOrWhiteSpace(stockInfo.LocationCode))
                        return WebResponseContent.Instance.Error($"托盘[{stockPalletCode}]库存已绑定货位,不能创建机械手{taskName}任务");
                }
                var section = App.Configuration.GetSection("RobotTaskAddressRules").GetSection(targetLineNo).GetChildren().Select(c => c.Value).ToArray();
                var task = new Dt_Task
                {
@@ -785,9 +803,9 @@
                    Roadway = stock.Roadway ?? string.Empty,
                    TaskType = taskType.GetHashCode(),
                    TaskStatus = TaskRobotStatusEnum.RobotNew.GetHashCode(),
                    SourceAddress = sourceLineNo,
                    TargetAddress = targetLineNo,
                    CurrentAddress = sourceLineNo,
                    SourceAddress = section[0],
                    TargetAddress = section[1],
                    CurrentAddress = targetLineNo,
                    NextAddress = targetLineNo,
                    WarehouseId = stockInfo?.WarehouseId ?? 1,
                    Grade = 1,
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
@@ -102,7 +102,7 @@
        }
        /// <summary>
        /// åˆ›å»ºç©ºæ‰˜ç›˜å‡ºåº“任务
        /// åˆ›å»ºç©ºæ‰˜ç›˜å…¥åº“任务
        /// </summary>
        /// <param name="taskDto"></param>
        /// <returns></returns>
@@ -113,6 +113,17 @@
        }
        /// <summary>
        /// ç©ºæ‰˜ç›˜å‡ºåº“完成
        /// </summary>
        /// <param name="taskDto"></param>
        /// <returns></returns>
        [HttpGet, HttpPost, Route("OutboundFinishTaskTray"), AllowAnonymous]
        public async Task<WebResponseContent?> OutboundFinishTaskTrayAsync([FromBody] CreateTaskDto taskDto)
        {
            return await Service.OutboundFinishTaskTrayAsync(taskDto);
        }
        /// <summary>
        /// ä¿®æ”¹ä»»åŠ¡çŠ¶æ€ï¼ˆæ ¹æ®ä»»åŠ¡ID修改为指定状态)
        /// </summary>
        /// <param name="taskDto"></param>
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json
@@ -74,6 +74,11 @@
    "BaseUrl": "http://localhost:5000",
    "Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjMwMTcyNzM5Mzk5NzYxOTIwIiwibmFtZSI6IlBBQ0voo4XphY3lt6XkvY0wMSIsIkZhY3RvcnlJZCI6IjEyMzQ1NiIsIlNpdGVJZCI6IjEyMzQ1NiIsIkNvZGUiOiJYWExQQUNLMDRBRTAzMiIsIm5iZiI6MTcwNDE4NzY5MCwiZXhwIjoyMTQ1NjkxNjkwLCJpc3MiOiJodHRwczovL3d3dy5oeW1zb24uY29tIiwiYXVkIjoiaHR0cHM6Ly93d3cuaHltc29uLmNvbSJ9.An1BE7UgfcSP--LtTOmmmWVE2RQFPDahLkDg1xy5KqY"
  },
  "AGV_OutTaskComplete": "http://localhost:9999/OutTaskComplete", // ä¸ŠæŠ¥AGV出库输送线完成
  "RobotTaskAddressRules": {
    "11068": [ "1", "2" ]
  },
  "A0GV_OutTaskComplete": "http://localhost:9999/OutTaskComplete", // ä¸ŠæŠ¥AGV出库输送线完成
  "WCS_ReceiveTask": "http://localhost:9292/api/Task/ReceiveTask" // WMS输送线任务下发
}
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/DTOs/WcsDeviceDto.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
using SqlSugar;
namespace WIDESEAWCS_S7Simulator.Application.DTOs;
/// <summary>
/// è®¾å¤‡ä¿¡æ¯ DTO(映射 WCS Dt_DeviceInfo è¡¨ï¼‰
/// </summary>
[SugarTable("Dt_DeviceInfo")]
public class WcsDeviceDto
{
    public int Id { get; set; }
    public string DeviceCode { get; set; } = string.Empty;
    public string DeviceName { get; set; } = string.Empty;
    public string DeviceType { get; set; } = string.Empty;
    public string DeviceStatus { get; set; } = string.Empty;
    public string DeviceIp { get; set; } = string.Empty;
    public int DevicePort { get; set; }
    public string DevicePlcType { get; set; } = string.Empty;
    public string DeviceRemark { get; set; } = string.Empty;
}
/// <summary>
/// è®¾å¤‡åè®® DTO(映射 WCS Dt_DeviceProtocol è¡¨ï¼‰
/// </summary>
[SugarTable("Dt_DeviceProtocol")]
public class WcsDeviceProtocolDto
{
    public int Id { get; set; }
    public int DeviceId { get; set; }
    public string DeviceChildCode { get; set; } = string.Empty;
    public string DeviceProDataBlock { get; set; } = string.Empty;
    public decimal DeviceProOffset { get; set; }
    public string DeviceProDataType { get; set; } = string.Empty;
    public int DeviceProDataLength { get; set; }
    public string DeviceProParamName { get; set; } = string.Empty;
    public string DeviceProParamType { get; set; } = string.Empty;
    public string DeviceProParamDes { get; set; } = string.Empty;
    public string DeviceProRemark { get; set; } = string.Empty;
}
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/DatabaseDeviceService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,74 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SqlSugar;
using WIDESEAWCS_S7Simulator.Application.DTOs;
namespace WIDESEAWCS_S7Simulator.Application;
/// <summary>
/// è¯»å– WCS æ•°æ®åº“设备数据
/// </summary>
public class DatabaseDeviceService : IDisposable
{
    private readonly WcsDbOptions _options;
    private readonly ILogger<DatabaseDeviceService> _logger;
    private readonly SqlSugarScope _db;
    public DatabaseDeviceService(IOptions<WcsDbOptions> options, ILogger<DatabaseDeviceService> logger)
    {
        _options = options.Value;
        _logger = logger;
        _db = new SqlSugarScope(new ConnectionConfig
        {
            ConnectionString = _options.ConnectionString,
            DbType = DbType.SqlServer,
            IsAutoCloseConnection = true,
            InitKeyType = InitKeyType.Attribute
        });
    }
    /// <summary>
    /// èŽ·å–æ‰€æœ‰ SiemensS7 ç±»åž‹çš„设备
    /// </summary>
    public async Task<List<WcsDeviceDto>> GetSiemensS7DevicesAsync()
    {
        try
        {
            var devices = await _db.Queryable<WcsDeviceDto>()
                .Where(d => d.DevicePlcType == "SiemensS7")
                .ToListAsync();
            _logger.LogInformation("从数据库获取到 {Count} ä¸ª SiemensS7 è®¾å¤‡", devices.Count);
            return devices;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "从数据库获取设备失败");
            return new List<WcsDeviceDto>();
        }
    }
    /// <summary>
    /// èŽ·å–æŒ‡å®šè®¾å¤‡çš„åè®®åˆ—è¡¨
    /// </summary>
    public async Task<List<WcsDeviceProtocolDto>> GetDeviceProtocolsAsync(int deviceId)
    {
        try
        {
            var protocols = await _db.Queryable<WcsDeviceProtocolDto>()
                .Where(p => p.DeviceId == deviceId)
                .ToListAsync();
            return protocols;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取设备协议失败 DeviceId={DeviceId}", deviceId);
            return new List<WcsDeviceProtocolDto>();
        }
    }
    public void Dispose()
    {
        _db.Dispose();
    }
}
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/InstanceSyncService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,170 @@
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
using WIDESEAWCS_S7Simulator.Application.Protocol;
using WIDESEAWCS_S7Simulator.Core.Entities;
using WIDESEAWCS_S7Simulator.Core.Enums;
using WIDESEAWCS_S7Simulator.Core.Interfaces;
using WIDESEAWCS_S7Simulator.Core.Protocol;
namespace WIDESEAWCS_S7Simulator.Application;
/// <summary>
/// å®žä¾‹åŒæ­¥æœåŠ¡ï¼šåè°ƒæ•°æ®åº“è¯»å–ã€åè®®æ¨¡æ¿ç”Ÿæˆã€å®žä¾‹åˆ›å»º
/// </summary>
public class InstanceSyncService
{
    private readonly DatabaseDeviceService _deviceService;
    private readonly ISimulatorInstanceManager _instanceManager;
    private readonly IProtocolTemplateService _templateService;
    private readonly ILogger<InstanceSyncService> _logger;
    private DateTime? _lastSyncTime;
    public DateTime? LastSyncTime => _lastSyncTime;
    public InstanceSyncService(
        DatabaseDeviceService deviceService,
        ISimulatorInstanceManager instanceManager,
        IProtocolTemplateService templateService,
        ILogger<InstanceSyncService> logger)
    {
        _deviceService = deviceService;
        _instanceManager = instanceManager;
        _templateService = templateService;
        _logger = logger;
    }
    /// <summary>
    /// æ‰§è¡ŒåŒæ­¥ï¼šä»Žæ•°æ®åº“读取设备,创建实例和协议模板
    /// </summary>
    public async Task SyncInstancesAsync()
    {
        _logger.LogInformation("开始同步实例...");
        _lastSyncTime = DateTime.Now;
        // 1. èŽ·å–æ‰€æœ‰ SiemensS7 è®¾å¤‡
        var devices = await _deviceService.GetSiemensS7DevicesAsync();
        if (devices.Count == 0)
        {
            _logger.LogWarning("未找到 SiemensS7 è®¾å¤‡ï¼ŒåŒæ­¥å–消");
            return;
        }
        // 2. èŽ·å–çŽ°æœ‰å®žä¾‹ID列表
        var existingInstanceIds = _instanceManager.GetAllInstances().Select(i => i.Config.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
        var dbDeviceCodes = devices.Select(d => d.DeviceCode).ToHashSet(StringComparer.OrdinalIgnoreCase);
        // 3. æ¸…理已不存在的实例(数据库中没有但内存中有)
        foreach (var instanceId in existingInstanceIds)
        {
            if (!dbDeviceCodes.Contains(instanceId))
            {
                _logger.LogInformation("数据库中已删除设备 {InstanceId},从内存移除", instanceId);
                await _instanceManager.DeleteInstanceAsync(instanceId, deleteConfig: false);
            }
        }
        // 4. åˆ›å»ºæˆ–更新实例
        foreach (var device in devices)
        {
            try
            {
                // 4.1 èŽ·å–è®¾å¤‡åè®®
                var protocols = await _deviceService.GetDeviceProtocolsAsync(device.Id);
                // é¢„计算一个缓存字典:协议对象 -> DB编号
                var dbCache = protocols.ToDictionary(
                    p => p,
                    p => int.TryParse(Regex.Match(p.DeviceProDataBlock, @"\d+").Value, out var n) ? n : 50
                );
                // 1. èŽ·å–å”¯ä¸€ç¼–å·åˆ—è¡¨
                var dbNumbers = dbCache.Values.Distinct().ToList();
                // 4.2 åˆ›å»ºåè®®æ¨¡æ¿
                var templateId = $"protocol-{device.DeviceCode}";
                var template = new ProtocolTemplate
                {
                    Id = templateId,
                    Name = $"{device.DeviceName} åè®®æ¨¡æ¿",
                    Version = "1.0",
                    Fields = protocols.Select(p => new ProtocolFieldMapping
                    {
                        FieldKey = $"{p.DeviceChildCode}_{p.DeviceProParamName}",
                        DbNumber = dbCache[p],
                        Offset = (int)p.DeviceProOffset,
                        Bit = 1,
                        DataType = MapDataType(p.DeviceProDataType),
                        Length = p.DeviceProDataLength,
                        Direction = ProtocolFieldDirection.Bidirectional
                    }).ToList()
                };
                await _templateService.UpsertAsync(template);
                // 4.3 åˆ›å»ºå®žä¾‹é…ç½®
                var config = new InstanceConfig
                {
                    Id = device.DeviceCode,
                    Name = device.DeviceName,
                    PLCType = SiemensPLCType.S71500,
                    Port = device.DevicePort,
                    AutoStart = false,
                    ProtocolTemplateId = templateId,
                    MemoryConfig = GetDefaultMemoryConfig(dbNumbers)
                };
                // 4.4 åˆ›å»ºæˆ–更新实例
                if (_instanceManager.InstanceExists(device.DeviceCode))
                {
                    _logger.LogInformation("实例 {DeviceCode} å·²å­˜åœ¨ï¼Œè·³è¿‡åˆ›å»º", device.DeviceCode);
                }
                else
                {
                    await _instanceManager.CreateInstanceAsync(config);
                    _logger.LogInformation("已创建实例 {DeviceCode} (端口:{Port})", device.DeviceCode, device.DevicePort);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "同步设备 {DeviceCode} å¤±è´¥", device.DeviceCode);
            }
        }
        _logger.LogInformation("同步完成");
    }
    /// <summary>
    /// æ•°æ®ç±»åž‹æ˜ å°„:数据库字符串 -> ProtocolDataType æžšä¸¾
    /// ProtocolDataType: Byte=0, Int=1, DInt=2, String=3, Bool=4
    /// </summary>
    private static ProtocolDataType MapDataType(string? dbDataType)
    {
        return dbDataType?.ToLowerInvariant() switch
        {
            "bit" => ProtocolDataType.Bool,       // Bool=4
            "byte" => ProtocolDataType.Byte,      // Byte=0
            "int" or "word" => ProtocolDataType.Int,  // Int=1
            "dint" => ProtocolDataType.DInt,       // DInt=2
            "string" or "string8" or "string16" => ProtocolDataType.String,  // String=3
            _ => ProtocolDataType.Byte
        };
    }
    /// <summary>
    /// èŽ·å–é»˜è®¤å†…å­˜é…ç½®
    /// </summary>
    private static MemoryRegionConfig GetDefaultMemoryConfig(List<int> dbBlockNumbers)
    {
        return new MemoryRegionConfig
        {
            MRegionSize = 1024,
            DBBlockCount = dbBlockNumbers.Count,
            DBBlockNumbers = dbBlockNumbers,
            DBBlockSize = 65536,
            IRegionSize = 256,
            QRegionSize = 256,
            TRegionCount = 64,
            CRegionCount = 64
        };
    }
}
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/WIDESEAWCS_S7Simulator.Application.csproj
@@ -8,6 +8,7 @@
    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0" />
    <PackageReference Include="SqlSugarCore" Version="5.1.4.170" />
  </ItemGroup>
  <PropertyGroup>
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Application/WcsDbOptions.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
namespace WIDESEAWCS_S7Simulator.Application;
/// <summary>
/// WCS æ•°æ®åº“连接配置
/// </summary>
public class WcsDbOptions
{
    public const string SectionName = "WcsDb";
    /// <summary>
    /// æ˜¯å¦å¯ç”¨æ•°æ®åº“同步
    /// </summary>
    public bool Enabled { get; set; }
    /// <summary>
    /// è¿žæŽ¥å­—符串
    /// </summary>
    public string ConnectionString { get; set; } = string.Empty;
    /// <summary>
    /// æ•°æ®åº“类型:SqlServer=2, MySql=1
    /// </summary>
    public int DbType { get; set; } = 2;
}
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Controllers/SyncController.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
using Microsoft.AspNetCore.Mvc;
using WIDESEAWCS_S7Simulator.Application;
namespace WIDESEAWCS_S7Simulator.Server.Controllers;
[ApiController]
[Route("api/[controller]")]
public class SyncController : ControllerBase
{
    private readonly InstanceSyncService _syncService;
    private readonly ILogger<SyncController> _logger;
    public SyncController(InstanceSyncService syncService, ILogger<SyncController> logger)
    {
        _syncService = syncService;
        _logger = logger;
    }
    /// <summary>
    /// æ‰‹åŠ¨è§¦å‘å®žä¾‹åŒæ­¥
    /// </summary>
    [HttpPost("SyncInstances")]
    public async Task<IActionResult> SyncInstances()
    {
        try
        {
            _logger.LogInformation("收到手动同步请求");
            await _syncService.SyncInstancesAsync();
            return Ok(new { message = "同步完成", lastSyncTime = _syncService.LastSyncTime });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "手动同步失败");
            return StatusCode(500, new { message = "同步失败", error = ex.Message });
        }
    }
    /// <summary>
    /// èŽ·å–ä¸Šæ¬¡åŒæ­¥æ—¶é—´
    /// </summary>
    [HttpGet("LastSyncTime")]
    public IActionResult GetLastSyncTime()
    {
        return Ok(new { lastSyncTime = _syncService.LastSyncTime });
    }
}
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-1001/config.json
ÎļþÃû´Ó Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-Line1/config.json ÐÞ¸Ä
@@ -1,11 +1,11 @@
{
  "id": "Line1",
  "name": "\u4E00\u53F7\u8F93\u9001\u7EBF",
  "id": "1001",
  "name": "A\u533A_\u4E00\u6CE8\u8F93\u9001\u7EBF",
  "plcType": "S71500",
  "port": 105,
  "activationKey": "4b86f3fc-f650-3b08-5924-b0f8278d6ed2",
  "autoStart": true,
  "protocolTemplateId": "wcs-line-v260202",
  "activationKey": "",
  "autoStart": false,
  "protocolTemplateId": "protocol-1001",
  "memoryConfig": {
    "mRegionSize": 1024,
    "dbBlockCount": 1,
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-1002/config.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
{
  "id": "1002",
  "name": "A\u533A_\u6362\u76D8\u8F93\u9001\u7EBF",
  "plcType": "S71500",
  "port": 108,
  "activationKey": "",
  "autoStart": false,
  "protocolTemplateId": "protocol-1002",
  "memoryConfig": {
    "mRegionSize": 1024,
    "dbBlockCount": 1,
    "dbBlockNumbers": [
      50
    ],
    "dbBlockSize": 65536,
    "iRegionSize": 256,
    "qRegionSize": 256,
    "tRegionCount": 64,
    "cRegionCount": 64
  }
}
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-1003/config.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
{
  "id": "1003",
  "name": "A\u533A_\u5E38\u6E29\u8F93\u9001\u7EBF",
  "plcType": "S71500",
  "port": 109,
  "activationKey": "",
  "autoStart": false,
  "protocolTemplateId": "protocol-1003",
  "memoryConfig": {
    "mRegionSize": 1024,
    "dbBlockCount": 1,
    "dbBlockNumbers": [
      50
    ],
    "dbBlockSize": 65536,
    "iRegionSize": 256,
    "qRegionSize": 256,
    "tRegionCount": 64,
    "cRegionCount": 64
  }
}
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-CWSC1/config.json
@@ -1,11 +1,11 @@
{
  "id": "CWSC1",
  "name": "\u5E38\u6E291\u53F7\u5806\u579B\u673A",
  "name": "\u5E38\u6E29\u5806\u579B\u673A",
  "plcType": "S71500",
  "port": 103,
  "activationKey": "4b86f3fc-f650-3b08-5924-b0f8278d6ed2",
  "autoStart": true,
  "protocolTemplateId": "stacker-interaction-v1",
  "activationKey": "",
  "autoStart": false,
  "protocolTemplateId": "protocol-CWSC1",
  "memoryConfig": {
    "mRegionSize": 1024,
    "dbBlockCount": 1,
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-CWSC1/memory.json
ÎļþÒÑɾ³ý
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-GWSC1/config.json
@@ -1,11 +1,11 @@
{
  "id": "GWSC1",
  "name": "\u9AD8\u6E291\u53F7\u5806\u579B\u673A",
  "name": "\u9AD8\u6E29\u5806\u579B\u673A",
  "plcType": "S71500",
  "port": 102,
  "activationKey": "4b86f3fc-f650-3b08-5924-b0f8278d6ed2",
  "autoStart": true,
  "protocolTemplateId": "stacker-interaction-v1",
  "activationKey": "",
  "autoStart": false,
  "protocolTemplateId": "protocol-GWSC1",
  "memoryConfig": {
    "mRegionSize": 1024,
    "dbBlockCount": 1,
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-GWSC1/memory.json
ÎļþÒÑɾ³ý
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-HCSC1/config.json
@@ -1,17 +1,17 @@
{
  "id": "HCSC1",
  "name": "\u5316\u62101\u53F7\u5806\u579B\u673A",
  "name": "\u5316\u6210\u5806\u579B\u673A",
  "plcType": "S71500",
  "port": 104,
  "activationKey": "4b86f3fc-f650-3b08-5924-b0f8278d6ed2",
  "autoStart": true,
  "protocolTemplateId": "plclink-stacker-v1",
  "activationKey": "",
  "autoStart": false,
  "protocolTemplateId": "protocol-HCSC1",
  "memoryConfig": {
    "mRegionSize": 1024,
    "dbBlockCount": 2,
    "dbBlockNumbers": [
      900,
      910
      910,
      900
    ],
    "dbBlockSize": 65536,
    "iRegionSize": 256,
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-HCSC1/memory.json
ÎļþÒÑɾ³ý
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-Line1/memory.json
ÎļþÒÑɾ³ý
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-Line2/config.json
ÎļþÒÑɾ³ý
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-PM/config.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
{
  "id": "PM",
  "name": "\u63D2\u62D4\u9489\u673A",
  "plcType": "S71500",
  "port": 107,
  "activationKey": "",
  "autoStart": false,
  "protocolTemplateId": "protocol-PM",
  "memoryConfig": {
    "mRegionSize": 1024,
    "dbBlockCount": 1,
    "dbBlockNumbers": [
      600
    ],
    "dbBlockSize": 65536,
    "iRegionSize": 256,
    "qRegionSize": 256,
    "tRegionCount": 64,
    "cRegionCount": 64
  }
}
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/protocol-templates.json
@@ -1,8 +1,1231 @@
[
  {
    "id": "wcs-line-v260202",
    "name": "WCS-\u8F93\u9001\u7EBF\u5BF9\u63A5\u534F\u8BAE V260202",
    "version": "V260202",
    "id": "protocol-GWSC1",
    "name": "\u9AD8\u6E29\u5806\u579B\u673A \u534F\u8BAE\u6A21\u677F",
    "version": "1.0",
    "fields": [
      {
        "fieldKey": "GWSC01_InputTaskNum",
        "dbNumber": 100,
        "offset": 194,
        "bit": 1,
        "dataType": 2,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_WorkAction",
        "dbNumber": 100,
        "offset": 192,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_InputWorkType",
        "dbNumber": 100,
        "offset": 198,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_InputPickRow",
        "dbNumber": 100,
        "offset": 208,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_InputPickColumn",
        "dbNumber": 100,
        "offset": 210,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_InputPickLayer",
        "dbNumber": 100,
        "offset": 212,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_InputPlaceRow",
        "dbNumber": 100,
        "offset": 214,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_InputPlaceColumn",
        "dbNumber": 100,
        "offset": 216,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_InputPlaceLayer",
        "dbNumber": 100,
        "offset": 218,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_StackerCraneAutoStatus",
        "dbNumber": 100,
        "offset": 2,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_StackerCraneWorkStatus",
        "dbNumber": 100,
        "offset": 4,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_OutputFaultHigh",
        "dbNumber": 100,
        "offset": 12,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_OutputFaultMid",
        "dbNumber": 100,
        "offset": 16,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_OutputFaultLow",
        "dbNumber": 100,
        "offset": 20,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_CurrentTaskNum",
        "dbNumber": 100,
        "offset": 24,
        "bit": 1,
        "dataType": 2,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_StackerCraneCompleted",
        "dbNumber": 100,
        "offset": 28,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_OutputCurrentColumn",
        "dbNumber": 100,
        "offset": 46,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_OutputCurrentLayer",
        "dbNumber": 100,
        "offset": 48,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "GWSC01_StackerCraneStatus",
        "dbNumber": 100,
        "offset": 96,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      }
    ]
  },
  {
    "id": "protocol-HCSC1",
    "name": "\u5316\u6210\u5806\u579B\u673A \u534F\u8BAE\u6A21\u677F",
    "version": "1.0",
    "fields": [
      {
        "fieldKey": "HCSC01_StackerCraneHeartBeat",
        "dbNumber": 910,
        "offset": 0,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_StackerCraneStatus",
        "dbNumber": 910,
        "offset": 6,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_StackerCraneFaultCode",
        "dbNumber": 910,
        "offset": 8,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_StackerCraneWorkStatus",
        "dbNumber": 910,
        "offset": 20,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_StackerCraneRow",
        "dbNumber": 910,
        "offset": 22,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_StackerCraneColumn",
        "dbNumber": 910,
        "offset": 24,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_StackerCraneLayer",
        "dbNumber": 910,
        "offset": 26,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_CurrentTaskNum",
        "dbNumber": 910,
        "offset": 28,
        "bit": 1,
        "dataType": 2,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_StackerCraneWorkAction",
        "dbNumber": 910,
        "offset": 40,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_StackerCraneCompleted",
        "dbNumber": 910,
        "offset": 42,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_HeartBeat",
        "dbNumber": 900,
        "offset": 0,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_TaskNum",
        "dbNumber": 900,
        "offset": 2,
        "bit": 1,
        "dataType": 2,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_WorkAction",
        "dbNumber": 900,
        "offset": 40,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_FireAlarm",
        "dbNumber": 900,
        "offset": 42,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_WorkType",
        "dbNumber": 900,
        "offset": 44,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_StartRow",
        "dbNumber": 900,
        "offset": 46,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_StartColumn",
        "dbNumber": 900,
        "offset": 48,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_StartLayer",
        "dbNumber": 900,
        "offset": 50,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_EndRow",
        "dbNumber": 900,
        "offset": 52,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_EndColumn",
        "dbNumber": 900,
        "offset": 54,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_EndLayer",
        "dbNumber": 900,
        "offset": 56,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "HCSC01_Barcode",
        "dbNumber": 900,
        "offset": 58,
        "bit": 1,
        "dataType": 3,
        "length": 26,
        "direction": 2
      }
    ]
  },
  {
    "id": "protocol-CWSC1",
    "name": "\u5E38\u6E29\u5806\u579B\u673A \u534F\u8BAE\u6A21\u677F",
    "version": "1.0",
    "fields": [
      {
        "fieldKey": "CWSC01_InputTaskNum",
        "dbNumber": 100,
        "offset": 194,
        "bit": 1,
        "dataType": 2,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_WorkAction",
        "dbNumber": 100,
        "offset": 192,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_InputWorkType",
        "dbNumber": 100,
        "offset": 198,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_InputPickRow",
        "dbNumber": 100,
        "offset": 208,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_InputPickColumn",
        "dbNumber": 100,
        "offset": 210,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_InputPickLayer",
        "dbNumber": 100,
        "offset": 212,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_InputPlaceRow",
        "dbNumber": 100,
        "offset": 214,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_InputPlaceColumn",
        "dbNumber": 100,
        "offset": 216,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_InputPlaceLayer",
        "dbNumber": 100,
        "offset": 218,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_StackerCraneAutoStatus",
        "dbNumber": 100,
        "offset": 2,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_StackerCraneWorkStatus",
        "dbNumber": 100,
        "offset": 4,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_OutputFaultHigh",
        "dbNumber": 100,
        "offset": 12,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_OutputFaultMid",
        "dbNumber": 100,
        "offset": 16,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_OutputFaultLow",
        "dbNumber": 100,
        "offset": 20,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_CurrentTaskNum",
        "dbNumber": 100,
        "offset": 24,
        "bit": 1,
        "dataType": 2,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_StackerCraneCompleted",
        "dbNumber": 100,
        "offset": 28,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_OutputCurrentColumn",
        "dbNumber": 100,
        "offset": 46,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_OutputCurrentLayer",
        "dbNumber": 100,
        "offset": 48,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "CWSC01_StackerCraneStatus",
        "dbNumber": 100,
        "offset": 96,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      }
    ]
  },
  {
    "id": "protocol-1001",
    "name": "A\u533A_\u4E00\u6CE8\u8F93\u9001\u7EBF \u534F\u8BAE\u6A21\u677F",
    "version": "1.0",
    "fields": [
      {
        "fieldKey": "11028_TaskNo",
        "dbNumber": 50,
        "offset": 0,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_Source",
        "dbNumber": 50,
        "offset": 2,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_Target",
        "dbNumber": 50,
        "offset": 4,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_BoxType",
        "dbNumber": 50,
        "offset": 6,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_CV_State",
        "dbNumber": 50,
        "offset": 7,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_CV_ERRCode",
        "dbNumber": 50,
        "offset": 8,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_WCS_STB",
        "dbNumber": 50,
        "offset": 9,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_WCS_ACK",
        "dbNumber": 50,
        "offset": 10,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_PLC_STB",
        "dbNumber": 50,
        "offset": 11,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_PLC_ACK",
        "dbNumber": 50,
        "offset": 12,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_PLC_REQ",
        "dbNumber": 50,
        "offset": 13,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 14,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_WCS_Special",
        "dbNumber": 50,
        "offset": 15,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_Equ_Auto",
        "dbNumber": 50,
        "offset": 16,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_Last_pallet",
        "dbNumber": 50,
        "offset": 17,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11028_Barcode",
        "dbNumber": 50,
        "offset": 18,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "direction": 2
      },
      {
        "fieldKey": "11042_TaskNo",
        "dbNumber": 50,
        "offset": 560,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_Source",
        "dbNumber": 50,
        "offset": 562,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_Target",
        "dbNumber": 50,
        "offset": 564,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_BoxType",
        "dbNumber": 50,
        "offset": 566,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_CV_State",
        "dbNumber": 50,
        "offset": 567,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_CV_ERRCode",
        "dbNumber": 50,
        "offset": 568,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_WCS_STB",
        "dbNumber": 50,
        "offset": 569,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_WCS_ACK",
        "dbNumber": 50,
        "offset": 570,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_PLC_STB",
        "dbNumber": 50,
        "offset": 571,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_PLC_ACK",
        "dbNumber": 50,
        "offset": 572,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_PLC_REQ",
        "dbNumber": 50,
        "offset": 573,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 574,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_WCS_Special",
        "dbNumber": 50,
        "offset": 575,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_Equ_Auto",
        "dbNumber": 50,
        "offset": 576,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_Last_pallet",
        "dbNumber": 50,
        "offset": 577,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11042_Barcode",
        "dbNumber": 50,
        "offset": 578,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "direction": 2
      },
      {
        "fieldKey": "11062_TaskNo",
        "dbNumber": 50,
        "offset": 1360,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_Source",
        "dbNumber": 50,
        "offset": 1362,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_Target",
        "dbNumber": 50,
        "offset": 1364,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_BoxType",
        "dbNumber": 50,
        "offset": 1366,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_CV_State",
        "dbNumber": 50,
        "offset": 1367,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_CV_ERRCode",
        "dbNumber": 50,
        "offset": 1368,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_WCS_STB",
        "dbNumber": 50,
        "offset": 1369,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_WCS_ACK",
        "dbNumber": 50,
        "offset": 1370,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_PLC_STB",
        "dbNumber": 50,
        "offset": 1371,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_PLC_ACK",
        "dbNumber": 50,
        "offset": 1372,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_PLC_REQ",
        "dbNumber": 50,
        "offset": 1373,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 1374,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_WCS_Special",
        "dbNumber": 50,
        "offset": 1375,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_Equ_Auto",
        "dbNumber": 50,
        "offset": 1376,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_Last_pallet",
        "dbNumber": 50,
        "offset": 1377,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11062_Barcode",
        "dbNumber": 50,
        "offset": 1378,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "direction": 2
      },
      {
        "fieldKey": "11068_TaskNo",
        "dbNumber": 50,
        "offset": 1600,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_Source",
        "dbNumber": 50,
        "offset": 1602,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_Target",
        "dbNumber": 50,
        "offset": 1604,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_BoxType",
        "dbNumber": 50,
        "offset": 1606,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_CV_State",
        "dbNumber": 50,
        "offset": 1607,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_CV_ERRCode",
        "dbNumber": 50,
        "offset": 1608,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_WCS_STB",
        "dbNumber": 50,
        "offset": 1609,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_WCS_ACK",
        "dbNumber": 50,
        "offset": 1610,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_PLC_STB",
        "dbNumber": 50,
        "offset": 1611,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_PLC_ACK",
        "dbNumber": 50,
        "offset": 1612,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_PLC_REQ",
        "dbNumber": 50,
        "offset": 1613,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 1614,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_WCS_Special",
        "dbNumber": 50,
        "offset": 1615,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_Equ_Auto",
        "dbNumber": 50,
        "offset": 1616,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_Last_pallet",
        "dbNumber": 50,
        "offset": 1617,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "11068_Barcode",
        "dbNumber": 50,
        "offset": 1618,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "direction": 2
      }
    ]
  },
  {
    "id": "protocol-PM",
    "name": "\u63D2\u62D4\u9489\u673A \u534F\u8BAE\u6A21\u677F",
    "version": "1.0",
    "fields": [
      {
        "fieldKey": "PinMachine_LogisticsLineRunningSignal",
        "dbNumber": 600,
        "offset": 0,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "PinMachine_PlugPinMachineRunningSignal",
        "dbNumber": 600,
        "offset": 2,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "PinMachine_MaterialRequestUpper",
        "dbNumber": 600,
        "offset": 4,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "PinMachine_OutputRequestUpper",
        "dbNumber": 600,
        "offset": 6,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "PinMachine_PlugPinTrayOutputReadyUpper",
        "dbNumber": 600,
        "offset": 8,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "PinMachine_MaterialRequestLower",
        "dbNumber": 600,
        "offset": 10,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "PinMachine_OutputRequestLower",
        "dbNumber": 600,
        "offset": 12,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "PinMachine_PlugPinTrayOutputReadyLower",
        "dbNumber": 600,
        "offset": 14,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      }
    ]
  },
  {
    "id": "protocol-1002",
    "name": "A\u533A_\u6362\u76D8\u8F93\u9001\u7EBF \u534F\u8BAE\u6A21\u677F",
    "version": "1.0",
    "fields": [
      {
        "fieldKey": "11001_TaskNo",
@@ -56,7 +1279,7 @@
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11001_WCS_STB",
@@ -65,7 +1288,7 @@
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11001_WCS_ACK",
@@ -74,7 +1297,7 @@
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11001_PLC_STB",
@@ -83,7 +1306,7 @@
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11001_PLC_ACK",
@@ -92,7 +1315,7 @@
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11001_PLC_REQ",
@@ -101,7 +1324,7 @@
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11001_WCS_ERRCode",
@@ -110,7 +1333,7 @@
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11001_WCS_Special",
@@ -119,7 +1342,7 @@
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11001_Equ_Auto",
@@ -128,7 +1351,7 @@
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11001_Last_pallet",
@@ -137,7 +1360,7 @@
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11001_Barcode",
@@ -146,832 +1369,449 @@
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_TaskNo",
        "dbNumber": 50,
        "offset": 450,
        "offset": 400,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_Source",
        "dbNumber": 50,
        "offset": 452,
        "offset": 402,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_Target",
        "dbNumber": 50,
        "offset": 454,
        "offset": 404,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_BoxType",
        "dbNumber": 50,
        "offset": 456,
        "offset": 406,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_CV_State",
        "dbNumber": 50,
        "offset": 457,
        "offset": 407,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_CV_ERRCode",
        "dbNumber": 50,
        "offset": 458,
        "offset": 408,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_WCS_STB",
        "dbNumber": 50,
        "offset": 459,
        "offset": 409,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_WCS_ACK",
        "dbNumber": 50,
        "offset": 460,
        "offset": 410,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_PLC_STB",
        "dbNumber": 50,
        "offset": 461,
        "offset": 411,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_PLC_ACK",
        "dbNumber": 50,
        "offset": 462,
        "offset": 412,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_PLC_REQ",
        "dbNumber": 50,
        "offset": 463,
        "offset": 413,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 464,
        "offset": 414,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_WCS_Special",
        "dbNumber": 50,
        "offset": 465,
        "offset": 415,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_Equ_Auto",
        "dbNumber": 50,
        "offset": 466,
        "offset": 416,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_Last_pallet",
        "dbNumber": 50,
        "offset": 467,
        "offset": 417,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11010_Barcode",
        "dbNumber": 50,
        "offset": 468,
        "offset": 418,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_TaskNo",
        "dbNumber": 50,
        "offset": 950,
        "offset": 800,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_Source",
        "dbNumber": 50,
        "offset": 952,
        "offset": 802,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_Target",
        "dbNumber": 50,
        "offset": 954,
        "offset": 804,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_BoxType",
        "dbNumber": 50,
        "offset": 956,
        "offset": 806,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_CV_State",
        "dbNumber": 50,
        "offset": 957,
        "offset": 807,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_CV_ERRCode",
        "dbNumber": 50,
        "offset": 958,
        "offset": 808,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_WCS_STB",
        "dbNumber": 50,
        "offset": 959,
        "offset": 809,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_WCS_ACK",
        "dbNumber": 50,
        "offset": 960,
        "offset": 810,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_PLC_STB",
        "dbNumber": 50,
        "offset": 961,
        "offset": 811,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_PLC_ACK",
        "dbNumber": 50,
        "offset": 962,
        "offset": 812,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_PLC_REQ",
        "dbNumber": 50,
        "offset": 963,
        "offset": 813,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 964,
        "offset": 814,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_WCS_Special",
        "dbNumber": 50,
        "offset": 965,
        "offset": 815,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_Equ_Auto",
        "dbNumber": 50,
        "offset": 966,
        "offset": 816,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_Last_pallet",
        "dbNumber": 50,
        "offset": 967,
        "offset": 817,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11020_Barcode",
        "dbNumber": 50,
        "offset": 968,
        "offset": 818,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_TaskNo",
        "dbNumber": 50,
        "offset": 1250,
        "offset": 1040,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_Source",
        "dbNumber": 50,
        "offset": 1252,
        "offset": 1042,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_Target",
        "dbNumber": 50,
        "offset": 1254,
        "offset": 1044,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_BoxType",
        "dbNumber": 50,
        "offset": 1256,
        "offset": 1046,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_CV_State",
        "dbNumber": 50,
        "offset": 1257,
        "offset": 1047,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_CV_ERRCode",
        "dbNumber": 50,
        "offset": 1258,
        "offset": 1048,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_WCS_STB",
        "dbNumber": 50,
        "offset": 1259,
        "offset": 1049,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_WCS_ACK",
        "dbNumber": 50,
        "offset": 1260,
        "offset": 1050,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_PLC_STB",
        "dbNumber": 50,
        "offset": 1261,
        "offset": 1051,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_PLC_ACK",
        "dbNumber": 50,
        "offset": 1262,
        "offset": 1052,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_PLC_REQ",
        "dbNumber": 50,
        "offset": 1263,
        "offset": 1053,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 1264,
        "offset": 1054,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_WCS_Special",
        "dbNumber": 50,
        "offset": 1265,
        "offset": 1055,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_Equ_Auto",
        "dbNumber": 50,
        "offset": 1266,
        "offset": 1056,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_Last_pallet",
        "dbNumber": 50,
        "offset": 1267,
        "offset": 1057,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 0
        "direction": 2
      },
      {
        "fieldKey": "11026_Barcode",
        "dbNumber": 50,
        "offset": 1268,
        "offset": 1058,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "direction": 0
        "direction": 2
      }
    ]
  },
  {
    "id": "plclink-stacker-v1",
    "name": "\u5316\u6210\u5806\u579B\u673A",
    "version": "2026.03",
    "fields": [
      {
        "fieldKey": "StackerCraneHeartBeat",
        "dbNumber": 910,
        "offset": 0,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "StackerCraneStatus",
        "dbNumber": 910,
        "offset": 6,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "StackerCraneFaultCode",
        "dbNumber": 910,
        "offset": 8,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "StackerCraneWorkStatus",
        "dbNumber": 910,
        "offset": 20,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "StackerCraneRow",
        "dbNumber": 910,
        "offset": 22,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "StackerCraneColumn",
        "dbNumber": 910,
        "offset": 24,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "StackerCraneLayer",
        "dbNumber": 910,
        "offset": 26,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "CurrentTaskNum",
        "dbNumber": 910,
        "offset": 28,
        "bit": 1,
        "dataType": 2,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "StackerCraneWorkAction",
        "dbNumber": 910,
        "offset": 40,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "StackerCraneCompleted",
        "dbNumber": 910,
        "offset": 42,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "HeartBeat",
        "dbNumber": 900,
        "offset": 0,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "TaskNum",
        "dbNumber": 900,
        "offset": 2,
        "bit": 1,
        "dataType": 2,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "WorkAction",
        "dbNumber": 900,
        "offset": 40,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "FireAlarm",
        "dbNumber": 900,
        "offset": 42,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "WorkType",
        "dbNumber": 900,
        "offset": 44,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "StartRow",
        "dbNumber": 900,
        "offset": 46,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "StartColumn",
        "dbNumber": 900,
        "offset": 48,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "StartLayer",
        "dbNumber": 900,
        "offset": 50,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "EndRow",
        "dbNumber": 900,
        "offset": 52,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "EndColumn",
        "dbNumber": 900,
        "offset": 54,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "EndLayer",
        "dbNumber": 900,
        "offset": 56,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "Barcode",
        "dbNumber": 900,
        "offset": 58,
        "bit": 1,
        "dataType": 2,
        "length": 26,
        "direction": 0
      }
    ]
  },
  {
    "id": "stacker-interaction-v1",
    "name": "\u9AD8\u5E38\u6E29\u5806\u579B\u673A",
    "version": "2026.03",
    "fields": [
      {
        "fieldKey": "InputTaskNum",
        "dbNumber": 100,
        "offset": 194,
        "bit": 1,
        "dataType": 2,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "WorkAction",
        "dbNumber": 100,
        "offset": 192,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "InputWorkType",
        "dbNumber": 100,
        "offset": 198,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "InputPickRow",
        "dbNumber": 100,
        "offset": 208,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "InputPickColumn",
        "dbNumber": 100,
        "offset": 210,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "InputPickLayer",
        "dbNumber": 100,
        "offset": 212,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "InputPlaceRow",
        "dbNumber": 100,
        "offset": 214,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "InputPlaceColumn",
        "dbNumber": 100,
        "offset": 216,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "InputPlaceLayer",
        "dbNumber": 100,
        "offset": 218,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 0
      },
      {
        "fieldKey": "StackerCraneAutoStatus",
        "dbNumber": 100,
        "offset": 2,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "StackerCraneWorkStatus",
        "dbNumber": 100,
        "offset": 4,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "OutputFaultHigh",
        "dbNumber": 100,
        "offset": 12,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "OutputFaultMid",
        "dbNumber": 100,
        "offset": 16,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "OutputFaultLow",
        "dbNumber": 100,
        "offset": 20,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "CurrentTaskNum",
        "dbNumber": 100,
        "offset": 24,
        "bit": 1,
        "dataType": 2,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "StackerCraneCompleted",
        "dbNumber": 100,
        "offset": 28,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "OutputCurrentColumn",
        "dbNumber": 100,
        "offset": 46,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "OutputCurrentLayer",
        "dbNumber": 100,
        "offset": 48,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      },
      {
        "fieldKey": "StackerCraneStatus",
        "dbNumber": 100,
        "offset": 96,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 1
      }
    ]
  },
  {
    "id": "wcs-line-hcline",
    "name": "WCS-\u5316\u6210\u6BB5\u8F93\u9001\u7EBF",
    "id": "protocol-1003",
    "name": "A\u533A_\u5E38\u6E29\u8F93\u9001\u7EBF \u534F\u8BAE\u6A21\u677F",
    "version": "1.0",
    "fields": [
      {
        "fieldKey": "10010_TaskNo",
        "fieldKey": "11070_TaskNo",
        "dbNumber": 50,
        "offset": 0,
        "bit": 1,
@@ -980,7 +1820,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_Source",
        "fieldKey": "11070_Source",
        "dbNumber": 50,
        "offset": 2,
        "bit": 1,
@@ -989,7 +1829,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_Target",
        "fieldKey": "11070_Target",
        "dbNumber": 50,
        "offset": 4,
        "bit": 1,
@@ -998,7 +1838,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_BoxType",
        "fieldKey": "11070_BoxType",
        "dbNumber": 50,
        "offset": 6,
        "bit": 1,
@@ -1007,7 +1847,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_CV_State",
        "fieldKey": "11070_CV_State",
        "dbNumber": 50,
        "offset": 7,
        "bit": 1,
@@ -1016,7 +1856,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_CV_ERRCode",
        "fieldKey": "11070_CV_ERRCode",
        "dbNumber": 50,
        "offset": 8,
        "bit": 1,
@@ -1025,7 +1865,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_WCS_STB",
        "fieldKey": "11070_WCS_STB",
        "dbNumber": 50,
        "offset": 9,
        "bit": 1,
@@ -1034,7 +1874,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_WCS_ACK",
        "fieldKey": "11070_WCS_ACK",
        "dbNumber": 50,
        "offset": 10,
        "bit": 1,
@@ -1043,7 +1883,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_PLC_STB",
        "fieldKey": "11070_PLC_STB",
        "dbNumber": 50,
        "offset": 11,
        "bit": 1,
@@ -1052,7 +1892,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_PLC_ACK",
        "fieldKey": "11070_PLC_ACK",
        "dbNumber": 50,
        "offset": 12,
        "bit": 1,
@@ -1061,7 +1901,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_PLC_REQ",
        "fieldKey": "11070_PLC_REQ",
        "dbNumber": 50,
        "offset": 13,
        "bit": 1,
@@ -1070,7 +1910,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_WCS_ERRCode",
        "fieldKey": "11070_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 14,
        "bit": 1,
@@ -1079,7 +1919,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_WCS_Special",
        "fieldKey": "11070_WCS_Special",
        "dbNumber": 50,
        "offset": 15,
        "bit": 1,
@@ -1088,7 +1928,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_Equ_Auto",
        "fieldKey": "11070_Equ_Auto",
        "dbNumber": 50,
        "offset": 16,
        "bit": 1,
@@ -1097,7 +1937,7 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_Last_pallet",
        "fieldKey": "11070_Last_pallet",
        "dbNumber": 50,
        "offset": 17,
        "bit": 1,
@@ -1106,1017 +1946,297 @@
        "direction": 2
      },
      {
        "fieldKey": "10010_Barcode",
        "fieldKey": "11070_Barcode",
        "dbNumber": 50,
        "offset": 18,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "length": 20,
        "direction": 2
      },
      {
        "fieldKey": "10030_TaskNo",
        "fieldKey": "11078_TaskNo",
        "dbNumber": 50,
        "offset": 50,
        "offset": 320,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_Source",
        "fieldKey": "11078_Source",
        "dbNumber": 50,
        "offset": 52,
        "offset": 322,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_Target",
        "fieldKey": "11078_Target",
        "dbNumber": 50,
        "offset": 54,
        "offset": 324,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_BoxType",
        "fieldKey": "11078_BoxType",
        "dbNumber": 50,
        "offset": 56,
        "offset": 326,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_CV_State",
        "fieldKey": "11078_CV_State",
        "dbNumber": 50,
        "offset": 57,
        "offset": 327,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_CV_ERRCode",
        "fieldKey": "11078_CV_ERRCode",
        "dbNumber": 50,
        "offset": 58,
        "offset": 328,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_WCS_STB",
        "fieldKey": "11078_WCS_STB",
        "dbNumber": 50,
        "offset": 59,
        "offset": 329,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_WCS_ACK",
        "fieldKey": "11078_WCS_ACK",
        "dbNumber": 50,
        "offset": 60,
        "offset": 330,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_PLC_STB",
        "fieldKey": "11078_PLC_STB",
        "dbNumber": 50,
        "offset": 61,
        "offset": 331,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_PLC_ACK",
        "fieldKey": "11078_PLC_ACK",
        "dbNumber": 50,
        "offset": 62,
        "offset": 332,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_PLC_REQ",
        "fieldKey": "11078_PLC_REQ",
        "dbNumber": 50,
        "offset": 63,
        "offset": 333,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_WCS_ERRCode",
        "fieldKey": "11078_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 64,
        "offset": 334,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_WCS_Special",
        "fieldKey": "11078_WCS_Special",
        "dbNumber": 50,
        "offset": 65,
        "offset": 335,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_Equ_Auto",
        "fieldKey": "11078_Equ_Auto",
        "dbNumber": 50,
        "offset": 66,
        "offset": 336,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_Last_pallet",
        "fieldKey": "11078_Last_pallet",
        "dbNumber": 50,
        "offset": 67,
        "offset": 337,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10030_Barcode",
        "fieldKey": "11078_Barcode",
        "dbNumber": 50,
        "offset": 68,
        "offset": 338,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "length": 20,
        "direction": 2
      },
      {
        "fieldKey": "10180_TaskNo",
        "fieldKey": "11085_TaskNo",
        "dbNumber": 50,
        "offset": 100,
        "offset": 600,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_Source",
        "fieldKey": "11085_Source",
        "dbNumber": 50,
        "offset": 102,
        "offset": 602,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_Target",
        "fieldKey": "11085_Target",
        "dbNumber": 50,
        "offset": 104,
        "offset": 604,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_BoxType",
        "fieldKey": "11085_BoxType",
        "dbNumber": 50,
        "offset": 106,
        "offset": 606,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_CV_State",
        "fieldKey": "11085_CV_State",
        "dbNumber": 50,
        "offset": 107,
        "offset": 607,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_CV_ERRCode",
        "fieldKey": "11085_CV_ERRCode",
        "dbNumber": 50,
        "offset": 108,
        "offset": 608,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_WCS_STB",
        "fieldKey": "11085_WCS_STB",
        "dbNumber": 50,
        "offset": 109,
        "offset": 609,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_WCS_ACK",
        "fieldKey": "11085_WCS_ACK",
        "dbNumber": 50,
        "offset": 110,
        "offset": 610,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_PLC_STB",
        "fieldKey": "11085_PLC_STB",
        "dbNumber": 50,
        "offset": 111,
        "offset": 611,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_PLC_ACK",
        "fieldKey": "11085_PLC_ACK",
        "dbNumber": 50,
        "offset": 112,
        "offset": 612,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_PLC_REQ",
        "fieldKey": "11085_PLC_REQ",
        "dbNumber": 50,
        "offset": 113,
        "offset": 613,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_WCS_ERRCode",
        "fieldKey": "11085_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 114,
        "offset": 614,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_WCS_Special",
        "fieldKey": "11085_WCS_Special",
        "dbNumber": 50,
        "offset": 115,
        "offset": 615,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_Equ_Auto",
        "fieldKey": "11085_Equ_Auto",
        "dbNumber": 50,
        "offset": 116,
        "offset": 616,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_Last_pallet",
        "fieldKey": "11085_Last_pallet",
        "dbNumber": 50,
        "offset": 117,
        "offset": 617,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10180_Barcode",
        "fieldKey": "11085_Barcode",
        "dbNumber": 50,
        "offset": 118,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "direction": 2
      },
      {
        "fieldKey": "10190_TaskNo",
        "dbNumber": 50,
        "offset": 150,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_Source",
        "dbNumber": 50,
        "offset": 152,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_Target",
        "dbNumber": 50,
        "offset": 154,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_BoxType",
        "dbNumber": 50,
        "offset": 156,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_CV_State",
        "dbNumber": 50,
        "offset": 157,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_CV_ERRCode",
        "dbNumber": 50,
        "offset": 158,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_WCS_STB",
        "dbNumber": 50,
        "offset": 159,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_WCS_ACK",
        "dbNumber": 50,
        "offset": 160,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_PLC_STB",
        "dbNumber": 50,
        "offset": 161,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_PLC_ACK",
        "dbNumber": 50,
        "offset": 162,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_PLC_REQ",
        "dbNumber": 50,
        "offset": 163,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 164,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_WCS_Special",
        "dbNumber": 50,
        "offset": 165,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_Equ_Auto",
        "dbNumber": 50,
        "offset": 166,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_Last_pallet",
        "dbNumber": 50,
        "offset": 167,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10190_Barcode",
        "dbNumber": 50,
        "offset": 168,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "direction": 2
      },
      {
        "fieldKey": "10250_TaskNo",
        "dbNumber": 50,
        "offset": 200,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_Source",
        "dbNumber": 50,
        "offset": 202,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_Target",
        "dbNumber": 50,
        "offset": 204,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_BoxType",
        "dbNumber": 50,
        "offset": 206,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_CV_State",
        "dbNumber": 50,
        "offset": 207,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_CV_ERRCode",
        "dbNumber": 50,
        "offset": 208,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_WCS_STB",
        "dbNumber": 50,
        "offset": 209,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_WCS_ACK",
        "dbNumber": 50,
        "offset": 210,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_PLC_STB",
        "dbNumber": 50,
        "offset": 211,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_PLC_ACK",
        "dbNumber": 50,
        "offset": 212,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_PLC_REQ",
        "dbNumber": 50,
        "offset": 213,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 214,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_WCS_Special",
        "dbNumber": 50,
        "offset": 215,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_Equ_Auto",
        "dbNumber": 50,
        "offset": 216,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_Last_pallet",
        "dbNumber": 50,
        "offset": 217,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "10250_Barcode",
        "dbNumber": 50,
        "offset": 218,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "direction": 2
      },
      {
        "fieldKey": "20090_TaskNo",
        "dbNumber": 50,
        "offset": 250,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_Source",
        "dbNumber": 50,
        "offset": 252,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_Target",
        "dbNumber": 50,
        "offset": 254,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_BoxType",
        "dbNumber": 50,
        "offset": 256,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_CV_State",
        "dbNumber": 50,
        "offset": 257,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_CV_ERRCode",
        "dbNumber": 50,
        "offset": 258,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_WCS_STB",
        "dbNumber": 50,
        "offset": 259,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_WCS_ACK",
        "dbNumber": 50,
        "offset": 260,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_PLC_STB",
        "dbNumber": 50,
        "offset": 261,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_PLC_ACK",
        "dbNumber": 50,
        "offset": 262,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_PLC_REQ",
        "dbNumber": 50,
        "offset": 263,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 264,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_WCS_Special",
        "dbNumber": 50,
        "offset": 265,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_Equ_Auto",
        "dbNumber": 50,
        "offset": 266,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_Last_pallet",
        "dbNumber": 50,
        "offset": 267,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20090_Barcode",
        "dbNumber": 50,
        "offset": 268,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "direction": 2
      },
      {
        "fieldKey": "20100_TaskNo",
        "dbNumber": 50,
        "offset": 300,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_Source",
        "dbNumber": 50,
        "offset": 302,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_Target",
        "dbNumber": 50,
        "offset": 304,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_BoxType",
        "dbNumber": 50,
        "offset": 306,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_CV_State",
        "dbNumber": 50,
        "offset": 307,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_CV_ERRCode",
        "dbNumber": 50,
        "offset": 308,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_WCS_STB",
        "dbNumber": 50,
        "offset": 309,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_WCS_ACK",
        "dbNumber": 50,
        "offset": 310,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_PLC_STB",
        "dbNumber": 50,
        "offset": 311,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_PLC_ACK",
        "dbNumber": 50,
        "offset": 312,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_PLC_REQ",
        "dbNumber": 50,
        "offset": 313,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 314,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_WCS_Special",
        "dbNumber": 50,
        "offset": 315,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_Equ_Auto",
        "dbNumber": 50,
        "offset": 316,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_Last_pallet",
        "dbNumber": 50,
        "offset": 317,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20100_Barcode",
        "dbNumber": 50,
        "offset": 318,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "direction": 2
      },
      {
        "fieldKey": "20170_TaskNo",
        "dbNumber": 50,
        "offset": 350,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_Source",
        "dbNumber": 50,
        "offset": 352,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_Target",
        "dbNumber": 50,
        "offset": 354,
        "bit": 1,
        "dataType": 1,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_BoxType",
        "dbNumber": 50,
        "offset": 356,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_CV_State",
        "dbNumber": 50,
        "offset": 357,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_CV_ERRCode",
        "dbNumber": 50,
        "offset": 358,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_WCS_STB",
        "dbNumber": 50,
        "offset": 359,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_WCS_ACK",
        "dbNumber": 50,
        "offset": 360,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_PLC_STB",
        "dbNumber": 50,
        "offset": 361,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_PLC_ACK",
        "dbNumber": 50,
        "offset": 362,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_PLC_REQ",
        "dbNumber": 50,
        "offset": 363,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 364,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_WCS_Special",
        "dbNumber": 50,
        "offset": 365,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_Equ_Auto",
        "dbNumber": 50,
        "offset": 366,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_Last_pallet",
        "dbNumber": 50,
        "offset": 367,
        "bit": 1,
        "dataType": 0,
        "length": 1,
        "direction": 2
      },
      {
        "fieldKey": "20170_Barcode",
        "dbNumber": 50,
        "offset": 368,
        "offset": 618,
        "bit": 1,
        "dataType": 3,
        "length": 22,
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Program.cs
@@ -5,6 +5,8 @@
using WIDESEAWCS_S7Simulator.Core.Entities;
using System.Text.Json.Serialization;
using WIDESEAWCS_S7Simulator.Application.Protocol;
using WIDESEAWCS_S7Simulator.Application;
using Microsoft.Extensions.Options;
using WIDESEAWCS_S7Simulator.Server.Services;
var builder = WebApplication.CreateBuilder(args);
@@ -39,6 +41,11 @@
// Register core services
builder.Services.AddSingleton<ISimulatorInstanceManager, SimulatorInstanceManager>();
// WCS æ•°æ®åº“配置
builder.Services.Configure<WcsDbOptions>(builder.Configuration.GetSection(WcsDbOptions.SectionName));
builder.Services.AddSingleton<DatabaseDeviceService>();
builder.Services.AddSingleton<InstanceSyncService>();
// Get content root path for data directory
var contentRoot = builder.Environment.ContentRootPath;
@@ -78,16 +85,26 @@
// å¯åŠ¨æ—¶åŠ è½½å·²ä¿å­˜çš„å®žä¾‹ï¼ˆä¸è‡ªåŠ¨å¯åŠ¨ï¼‰
var instanceManager = app.Services.GetRequiredService<ISimulatorInstanceManager>();
var syncService = app.Services.GetRequiredService<InstanceSyncService>();
_ = Task.Run(async () =>
{
    try
    {
        // å…ˆåŠ è½½å·²ä¿å­˜çš„å®žä¾‹
        await instanceManager.LoadSavedInstancesAsync(autoStart: false);
        Console.WriteLine($"Loaded {instanceManager.GetAllInstances().Count()} saved instances.");
        // å¦‚果启用了 WCS æ•°æ®åº“同步,则执行同步
        var wcsDbOptions = app.Services.GetRequiredService<IOptions<WcsDbOptions>>().Value;
        if (wcsDbOptions.Enabled)
        {
            await syncService.SyncInstancesAsync();
            Console.WriteLine($"WCS DB sync completed. Last sync: {syncService.LastSyncTime}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error loading saved instances: {ex.Message}");
        Console.WriteLine($"Error during startup: {ex.Message}");
    }
});
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/appsettings.json
@@ -104,5 +104,10 @@
      }
    ]
  },
  "WcsDb": {
  "Enabled": true,
  "ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWCS_ShanMei;User ID=sa;Password=P@ssw0rd;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
  "DbType": 2
},
  "AllowedHosts": "*"
}
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/docs/superpowers/plans/2026-04-08-database-instance-sync-plan.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,593 @@
# S7 æ¨¡æ‹Ÿå™¨æ•°æ®åº“实例同步实现计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** ä»Ž WCS æ•°æ®åº“自动同步设备到 S7 æ¨¡æ‹Ÿå™¨å®žä¾‹ï¼Œå¯åŠ¨æ—¶åŒæ­¥ä¸€æ¬¡ï¼Œä¹Ÿæ”¯æŒ API æ‰‹åŠ¨è§¦å‘
**Architecture:** åœ¨ Application å±‚新增 DatabaseDeviceService è¯»å– WCS æ•°æ®åº“,InstanceSyncService åè°ƒåŒæ­¥é€»è¾‘,SimulatorInstanceManager åˆ›å»º/管理实例。协议模板通过现有 IProtocolTemplateService ä¿å­˜åˆ°æœ¬åœ° JSON æ–‡ä»¶ã€‚
**Tech Stack:** .NET 6, SqlSugarCore, ASP.NET Core, JSON æ–‡ä»¶æŒä¹…化
---
## æ–‡ä»¶ç»“æž„
```
Application/
├── DTOs/
│   â””── WcsDeviceDto.cs           # æ•°æ®åº“字段映射 DTO
├── WcsDbOptions.cs               # WCS æ•°æ®åº“配置
├── DatabaseDeviceService.cs      # è¯»å– WCS æ•°æ®åº“
└── InstanceSyncService.cs         # åŒæ­¥åè°ƒé€»è¾‘
Server/
├── Controllers/
│   â””── SyncController.cs         # API æŽ¥å£
├── appsettings.json               # æ·»åŠ  WcsDb é…ç½®
└── Program.cs                     # æ³¨å†ŒæœåŠ¡ã€å¯åŠ¨åŒæ­¥
```
---
## Task 1: æ·»åŠ  SqlSugarCore ä¾èµ–
**Files:**
- Modify: `WIDESEAWCS_S7Simulator.Application/WIDESEAWCS_S7Simulator.Application.csproj`
- [ ] **Step 1: æ·»åŠ  SqlSugarCore NuGet åŒ…**
```xml
<PackageReference Include="SqlSugarCore" Version="5.1.4.170" />
```
- [ ] **Step 2: Commit**
```bash
git add WIDESEAWCS_S7Simulator.Application/WIDESEAWCS_S7Simulator.Application.csproj
git commit -m "feat: æ·»åŠ  SqlSugarCore ä¾èµ–用于数据库连接"
```
---
## Task 2: åˆ›å»º WCS æ•°æ®åº“ DTO
**Files:**
- Create: `WIDESEAWCS_S7Simulator.Application/DTOs/WcsDeviceDto.cs`
- [ ] **Step 1: åˆ›å»º WcsDeviceDto.cs**
```csharp
namespace WIDESEAWCS_S7Simulator.Application.DTOs;
/// <summary>
/// è®¾å¤‡ä¿¡æ¯ DTO(映射 WCS Dt_DeviceInfo è¡¨ï¼‰
/// </summary>
public class WcsDeviceDto
{
    public int Id { get; set; }
    public string DeviceCode { get; set; } = string.Empty;
    public string DeviceName { get; set; } = string.Empty;
    public string DeviceType { get; set; } = string.Empty;
    public string DeviceStatus { get; set; } = string.Empty;
    public string DeviceIp { get; set; } = string.Empty;
    public int DevicePort { get; set; }
    public string DevicePlcType { get; set; } = string.Empty;
    public string DeviceRemark { get; set; } = string.Empty;
}
/// <summary>
/// è®¾å¤‡åè®® DTO(映射 WCS Dt_DeviceProtocol è¡¨ï¼‰
/// </summary>
public class WcsDeviceProtocolDto
{
    public int Id { get; set; }
    public int DeviceId { get; set; }
    public string DeviceChildCode { get; set; } = string.Empty;
    public string DeviceProDataBlock { get; set; } = string.Empty;
    public decimal DeviceProOffset { get; set; }
    public string DeviceProDataType { get; set; } = string.Empty;
    public int DeviceProDataLength { get; set; }
    public string DeviceProParamName { get; set; } = string.Empty;
    public string DeviceProParamType { get; set; } = string.Empty;
    public string DeviceProParamDes { get; set; } = string.Empty;
    public string DeviceProRemark { get; set; } = string.Empty;
}
```
- [ ] **Step 2: Commit**
```bash
git add WIDESEAWCS_S7Simulator.Application/DTOs/WcsDeviceDto.cs
git commit -m "feat: æ·»åŠ  WCS è®¾å¤‡ DTO"
```
---
## Task 3: åˆ›å»º WcsDbOptions é…ç½®ç±»
**Files:**
- Create: `WIDESEAWCS_S7Simulator.Application/WcsDbOptions.cs`
- [ ] **Step 1: åˆ›å»º WcsDbOptions.cs**
```csharp
namespace WIDESEAWCS_S7Simulator.Application;
/// <summary>
/// WCS æ•°æ®åº“连接配置
/// </summary>
public class WcsDbOptions
{
    public const string SectionName = "WcsDb";
    /// <summary>
    /// æ˜¯å¦å¯ç”¨æ•°æ®åº“同步
    /// </summary>
    public bool Enabled { get; set; }
    /// <summary>
    /// è¿žæŽ¥å­—符串
    /// </summary>
    public string ConnectionString { get; set; } = string.Empty;
    /// <summary>
    /// æ•°æ®åº“类型:SqlServer=2, MySql=1
    /// </summary>
    public int DbType { get; set; } = 2;
}
```
- [ ] **Step 2: Commit**
```bash
git add WIDESEAWCS_S7Simulator.Application/WcsDbOptions.cs
git commit -m "feat: æ·»åŠ  WcsDbOptions é…ç½®ç±»"
```
---
## Task 4: åˆ›å»º DatabaseDeviceService
**Files:**
- Create: `WIDESEAWCS_S7Simulator.Application/DatabaseDeviceService.cs`
- [ ] **Step 1: åˆ›å»º DatabaseDeviceService.cs**
```csharp
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SqlSugar;
using WIDESEAWCS_S7Simulator.Application.DTOs;
namespace WIDESEAWCS_S7Simulator.Application;
/// <summary>
/// è¯»å– WCS æ•°æ®åº“设备数据
/// </summary>
public class DatabaseDeviceService : IDisposable
{
    private readonly WcsDbOptions _options;
    private readonly ILogger<DatabaseDeviceService> _logger;
    private readonly SqlSugarScope _db;
    public DatabaseDeviceService(IOptions<WcsDbOptions> options, ILogger<DatabaseDeviceService> logger)
    {
        _options = options.Value;
        _logger = logger;
        _db = new SqlSugarScope(new ConnectionConfig
        {
            ConnectionString = _options.ConnectionString,
            DbType = (DbType)_options.DbType,
            IsAutoCloseConnection = true,
            InitKeyType = InitKeyType.Attribute
        });
    }
    /// <summary>
    /// èŽ·å–æ‰€æœ‰ SiemensS7 ç±»åž‹çš„设备
    /// </summary>
    public async Task<List<WcsDeviceDto>> GetSiemensS7DevicesAsync()
    {
        try
        {
            var devices = await _db.Queryable<WcsDeviceDto>()
                .Where(d => d.DevicePlcType == "SiemensS7")
                .ToListAsync();
            _logger.LogInformation("从数据库获取到 {Count} ä¸ª SiemensS7 è®¾å¤‡", devices.Count);
            return devices;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "从数据库获取设备失败");
            return new List<WcsDeviceDto>();
        }
    }
    /// <summary>
    /// èŽ·å–æŒ‡å®šè®¾å¤‡çš„åè®®åˆ—è¡¨
    /// </summary>
    public async Task<List<WcsDeviceProtocolDto>> GetDeviceProtocolsAsync(int deviceId)
    {
        try
        {
            var protocols = await _db.Queryable<WcsDeviceProtocolDto>()
                .Where(p => p.DeviceId == deviceId)
                .ToListAsync();
            return protocols;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取设备协议失败 DeviceId={DeviceId}", deviceId);
            return new List<WcsDeviceProtocolDto>();
        }
    }
    public void Dispose()
    {
        _db.Dispose();
    }
}
```
- [ ] **Step 2: Commit**
```bash
git add WIDESEAWCS_S7Simulator.Application/DatabaseDeviceService.cs
git commit -m "feat: æ·»åŠ  DatabaseDeviceService è¯»å– WCS æ•°æ®åº“"
```
---
## Task 5: åˆ›å»º InstanceSyncService
**Files:**
- Create: `WIDESEAWCS_S7Simulator.Application/InstanceSyncService.cs`
- [ ] **Step 1: åˆ›å»º InstanceSyncService.cs**
```csharp
using Microsoft.Extensions.Logging;
using WIDESEAWCS_S7Simulator.Application.Protocol;
using WIDESEAWCS_S7Simulator.Core.Entities;
using WIDESEAWCS_S7Simulator.Core.Enums;
using WIDESEAWCS_S7Simulator.Core.Interfaces;
using WIDESEAWCS_S7Simulator.Core.Protocol;
namespace WIDESEAWCS_S7Simulator.Application;
/// <summary>
/// å®žä¾‹åŒæ­¥æœåŠ¡ï¼šåè°ƒæ•°æ®åº“è¯»å–ã€åè®®æ¨¡æ¿ç”Ÿæˆã€å®žä¾‹åˆ›å»º
/// </summary>
public class InstanceSyncService
{
    private readonly DatabaseDeviceService _deviceService;
    private readonly ISimulatorInstanceManager _instanceManager;
    private readonly IProtocolTemplateService _templateService;
    private readonly ILogger<InstanceSyncService> _logger;
    private DateTime? _lastSyncTime;
    public DateTime? LastSyncTime => _lastSyncTime;
    public InstanceSyncService(
        DatabaseDeviceService deviceService,
        ISimulatorInstanceManager instanceManager,
        IProtocolTemplateService templateService,
        ILogger<InstanceSyncService> logger)
    {
        _deviceService = deviceService;
        _instanceManager = instanceManager;
        _templateService = templateService;
        _logger = logger;
    }
    /// <summary>
    /// æ‰§è¡ŒåŒæ­¥ï¼šä»Žæ•°æ®åº“读取设备,创建实例和协议模板
    /// </summary>
    public async Task SyncInstancesAsync()
    {
        _logger.LogInformation("开始同步实例...");
        _lastSyncTime = DateTime.Now;
        // 1. èŽ·å–æ‰€æœ‰ SiemensS7 è®¾å¤‡
        var devices = await _deviceService.GetSiemensS7DevicesAsync();
        if (devices.Count == 0)
        {
            _logger.LogWarning("未找到 SiemensS7 è®¾å¤‡ï¼ŒåŒæ­¥å–消");
            return;
        }
        // 2. èŽ·å–çŽ°æœ‰å®žä¾‹ID列表
        var existingInstanceIds = _instanceManager.GetAllInstances().Select(i => i.Config.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
        var dbDeviceCodes = devices.Select(d => d.DeviceCode).ToHashSet(StringComparer.OrdinalIgnoreCase);
        // 3. æ¸…理已不存在的实例(数据库中没有但内存中有)
        foreach (var instanceId in existingInstanceIds)
        {
            if (!dbDeviceCodes.Contains(instanceId))
            {
                _logger.LogInformation("数据库中已删除设备 {InstanceId},从内存移除", instanceId);
                await _instanceManager.DeleteInstanceAsync(instanceId, deleteConfig: false);
            }
        }
        // 4. åˆ›å»ºæˆ–更新实例
        foreach (var device in devices)
        {
            try
            {
                // 4.1 èŽ·å–è®¾å¤‡åè®®
                var protocols = await _deviceService.GetDeviceProtocolsAsync(device.Id);
                // 4.2 åˆ›å»ºåè®®æ¨¡æ¿
                var templateId = $"protocol-{device.DeviceCode}";
                var template = new ProtocolTemplate
                {
                    Id = templateId,
                    Name = $"{device.DeviceName} åè®®æ¨¡æ¿",
                    Version = "1.0",
                    Fields = protocols.Select(p => new ProtocolFieldMapping
                    {
                        FieldKey = p.DeviceProParamName,
                        DbNumber = int.TryParse(p.DeviceProDataBlock, out var dbNum) ? dbNum : 50,
                        Offset = (int)p.DeviceProOffset,
                        Bit = 1,
                        DataType = MapDataType(p.DeviceProDataType),
                        Length = p.DeviceProDataLength,
                        Direction = ProtocolFieldDirection.Bidirectional
                    }).ToList()
                };
                await _templateService.UpsertAsync(template);
                // 4.3 åˆ›å»ºå®žä¾‹é…ç½®
                var config = new InstanceConfig
                {
                    Id = device.DeviceCode,
                    Name = device.DeviceName,
                    PLCType = SiemensPLCType.S71500, // é»˜è®¤ S7-1500,可根据需要扩展
                    Port = device.DevicePort,
                    AutoStart = false,
                    ProtocolTemplateId = templateId,
                    MemoryConfig = GetDefaultMemoryConfig()
                };
                // 4.4 åˆ›å»ºæˆ–更新实例
                if (_instanceManager.InstanceExists(device.DeviceCode))
                {
                    _logger.LogInformation("实例 {DeviceCode} å·²å­˜åœ¨ï¼Œè·³è¿‡åˆ›å»º", device.DeviceCode);
                }
                else
                {
                    await _instanceManager.CreateInstanceAsync(config);
                    _logger.LogInformation("已创建实例 {DeviceCode} (端口:{Port})", device.DeviceCode, device.DevicePort);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "同步设备 {DeviceCode} å¤±è´¥", device.DeviceCode);
            }
        }
        _logger.LogInformation("同步完成");
    }
    /// <summary>
    /// æ•°æ®ç±»åž‹æ˜ å°„:数据库字符串 -> ProtocolDataType æžšä¸¾
    /// ProtocolDataType: Byte=0, Int=1, DInt=2, String=3, Bool=4
    /// </summary>
    private static ProtocolDataType MapDataType(string? dbDataType)
    {
        return dbDataType?.ToLowerInvariant() switch
        {
            "bit" => ProtocolDataType.Bool,       // Bool=4
            "byte" => ProtocolDataType.Byte,       // Byte=0
            "int" or "word" => ProtocolDataType.Int,  // Int=1
            "dint" => ProtocolDataType.DInt,       // DInt=2
            "string" or "string8" or "string16" => ProtocolDataType.String,  // String=3
            _ => ProtocolDataType.Byte
        };
    }
    /// <summary>
    /// èŽ·å–é»˜è®¤å†…å­˜é…ç½®
    /// </summary>
    private static MemoryRegionConfig GetDefaultMemoryConfig()
    {
        return new MemoryRegionConfig
        {
            MRegionSize = 1024,
            DBBlockCount = 1,
            DBBlockNumbers = new List<int> { 50 },
            DBBlockSize = 65536,
            IRegionSize = 256,
            QRegionSize = 256,
            TRegionCount = 64,
            CRegionCount = 64
        };
    }
}
```
- [ ] **Step 2: Commit**
```bash
git add WIDESEAWCS_S7Simulator.Application/InstanceSyncService.cs
git commit -m "feat: æ·»åŠ  InstanceSyncService åŒæ­¥åè°ƒé€»è¾‘"
```
---
## Task 6: åˆ›å»º SyncController
**Files:**
- Create: `WIDESEAWCS_S7Simulator.Server/Controllers/SyncController.cs`
- [ ] **Step 1: åˆ›å»º SyncController.cs**
```csharp
using Microsoft.AspNetCore.Mvc;
using WIDESEAWCS_S7Simulator.Application;
namespace WIDESEAWCS_S7Simulator.Server.Controllers;
[ApiController]
[Route("api/[controller]")]
public class SyncController : ControllerBase
{
    private readonly InstanceSyncService _syncService;
    private readonly ILogger<SyncController> _logger;
    public SyncController(InstanceSyncService syncService, ILogger<SyncController> logger)
    {
        _syncService = syncService;
        _logger = logger;
    }
    /// <summary>
    /// æ‰‹åŠ¨è§¦å‘å®žä¾‹åŒæ­¥
    /// </summary>
    [HttpPost("SyncInstances")]
    public async Task<IActionResult> SyncInstances()
    {
        try
        {
            _logger.LogInformation("收到手动同步请求");
            await _syncService.SyncInstancesAsync();
            return Ok(new { message = "同步完成", lastSyncTime = _syncService.LastSyncTime });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "手动同步失败");
            return StatusCode(500, new { message = "同步失败", error = ex.Message });
        }
    }
    /// <summary>
    /// èŽ·å–ä¸Šæ¬¡åŒæ­¥æ—¶é—´
    /// </summary>
    [HttpGet("LastSyncTime")]
    public IActionResult GetLastSyncTime()
    {
        return Ok(new { lastSyncTime = _syncService.LastSyncTime });
    }
}
```
- [ ] **Step 2: Commit**
```bash
git add WIDESEAWCS_S7Simulator.Server/Controllers/SyncController.cs
git commit -m "feat: æ·»åŠ  SyncController API æŽ¥å£"
```
---
## Task 7: ä¿®æ”¹ appsettings.json æ·»åŠ  WcsDb é…ç½®
**Files:**
- Modify: `WIDESEAWCS_S7Simulator.Server/appsettings.json`
- [ ] **Step 1: æ·»åŠ  WcsDb é…ç½®èŠ‚**
在 appsettings.json æ ¹å¯¹è±¡æ·»åŠ ï¼š
```json
"WcsDb": {
  "Enabled": true,
  "ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWCS_ShanMei;User ID=sa;Password=${WCS_DB_PASSWORD};Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
  "DbType": 2
}
```
注意:`${WCS_DB_PASSWORD}` ä¸ºçŽ¯å¢ƒå˜é‡å ä½ç¬¦ï¼Œå®žé™…éƒ¨ç½²æ—¶æ›¿æ¢ä¸ºçœŸå®žå¯†ç æˆ–é€šè¿‡çŽ¯å¢ƒå˜é‡æ³¨å…¥ã€‚
- [ ] **Step 2: Commit**
```bash
git add WIDESEAWCS_S7Simulator.Server/appsettings.json
git commit -m "feat: æ·»åŠ  WcsDb æ•°æ®åº“配置"
```
---
## Task 8: ä¿®æ”¹ Program.cs æ³¨å†ŒæœåŠ¡å’Œå¯åŠ¨åŒæ­¥
**Files:**
- Modify: `WIDESEAWCS_S7Simulator.Server/Program.cs`
- [ ] **Step 1: æ³¨å†ŒæœåŠ¡å¹¶å¯åŠ¨æ—¶åŒæ­¥**
在 `builder.Services.AddControllers()` ä¹‹åŽæ·»åŠ ï¼š
```csharp
// WCS æ•°æ®åº“配置
builder.Services.Configure<WcsDbOptions>(builder.Configuration.GetSection(WcsDbOptions.SectionName));
builder.Services.AddSingleton<DatabaseDeviceService>();
builder.Services.AddSingleton<InstanceSyncService>();
```
在 `app.MapControllers()` ä¹‹åŽï¼Œå°†åŒæ­¥æ•´åˆåˆ°çŽ°æœ‰çš„ `Task.Run` ä¸­ï¼š
```csharp
// å¯åŠ¨æ—¶åŠ è½½å·²ä¿å­˜çš„å®žä¾‹ï¼ˆä¸è‡ªåŠ¨å¯åŠ¨ï¼‰
var instanceManager = app.Services.GetRequiredService<ISimulatorInstanceManager>();
var syncService = app.Services.GetRequiredService<InstanceSyncService>();
_ = Task.Run(async () =>
{
    try
    {
        // å…ˆåŠ è½½å·²ä¿å­˜çš„å®žä¾‹
        await instanceManager.LoadSavedInstancesAsync(autoStart: false);
        Console.WriteLine($"Loaded {instanceManager.GetAllInstances().Count()} saved instances.");
        // å¦‚果启用了 WCS æ•°æ®åº“同步,则执行同步
        var wcsDbOptions = app.Services.GetRequiredService<IOptions<WcsDbOptions>>().Value;
        if (wcsDbOptions.Enabled)
        {
            await syncService.SyncInstancesAsync();
            Console.WriteLine($"WCS DB sync completed. Last sync: {syncService.LastSyncTime}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error during startup: {ex.Message}");
    }
});
```
- [ ] **Step 2: Commit**
```bash
git add WIDESEAWCS_S7Simulator.Server/Program.cs
git commit -m "feat: Program.cs æ³¨å†ŒåŒæ­¥æœåŠ¡å¹¶å¯åŠ¨æ—¶åŒæ­¥"
```
---
## Task 9: æž„建验证
- [ ] **Step 1: è¿è¡Œ dotnet build éªŒè¯ç¼–译**
```bash
cd WIDESEAWCS_S7Simulator.Server
dotnet build
```
预期:无编译错误
- [ ] **Step 2: Commit any last changes if needed**
---
## æ‰§è¡Œé¡ºåº
1. Task 1 - æ·»åŠ  SqlSugarCore ä¾èµ–
2. Task 2 - åˆ›å»º WCS æ•°æ®åº“ DTO
3. Task 3 - åˆ›å»º WcsDbOptions é…ç½®ç±»
4. Task 4 - åˆ›å»º DatabaseDeviceService
5. Task 5 - åˆ›å»º InstanceSyncService
6. Task 6 - åˆ›å»º SyncController
7. Task 7 - ä¿®æ”¹ appsettings.json
8. Task 8 - ä¿®æ”¹ Program.cs
9. Task 9 - æž„建验证
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/docs/superpowers/specs/2026-04-08-database-instance-sync-design.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,142 @@
# S7 æ¨¡æ‹Ÿå™¨æ•°æ®åº“实例同步设计
## èƒŒæ™¯
S7 æ¨¡æ‹Ÿå™¨å½“前通过本地 JSON æ–‡ä»¶æ‰‹åŠ¨é…ç½®å®žä¾‹å’Œåè®®æ¨¡æ¿ï¼Œå·¥ä½œé‡å¤§ä¸”å®¹æ˜“å‡ºé”™ã€‚WCS ç³»ç»Ÿå·²æœ‰å®Œæ•´çš„设备信息数据库(SQL Server),包含 `Dt_DeviceInfo` å’Œ `Dt_DeviceProtocol` è¡¨ã€‚需要实现从数据库自动同步设备到 S7 æ¨¡æ‹Ÿå™¨å®žä¾‹ã€‚
## éœ€æ±‚
1. **数据源**:`Dt_DeviceInfo.DevicePlcType = 'SiemensS7'`
2. **协议模板**:每个设备生成一个协议模板,包含该设备所有 `Dt_DeviceProtocol` å­—段
3. **内存配置**:统一使用默认值(M区1024字节,DB块1个,DB块大小65536)
4. **同步时机**:启动时自动同步 + API æ‰‹åŠ¨è§¦å‘
## æ•´ä½“æž¶æž„
```
启动时 / API触发
       â”‚
       â–¼
┌─────────────────────────────┐
│  InstanceSyncService        â”‚
│  (Application å±‚)           â”‚
└─────────────┬───────────────┘
              â”‚ è¯»å– Dt_DeviceInfo (DevicePlcType='SiemensS7')
              â”‚ è¯»å– Dt_DeviceProtocol (按 DeviceId å…³è”)
              â–¼
┌─────────────────────────────┐
│  DatabaseDeviceService      â”‚
│  (Application å±‚)           â”‚
│  - SqlSugar è¿žæŽ¥ WCS DB      â”‚
└─────────────┬───────────────┘
              â”‚ InstanceConfig + ProtocolTemplate
              â–¼
┌─────────────────────────────┐
│  SimulatorInstanceManager   â”‚
│  - åˆ›å»º/更新实例             â”‚
│  - åº”用内存默认值            â”‚
└─────────────────────────────┘
```
## æ–°å¢žç»„ä»¶
| ç»„ä»¶ | ä½ç½® | èŒè´£ |
|------|------|------|
| `WcsDbOptions` | Application | æ•°æ®åº“连接配置类 |
| `DatabaseDeviceService` | Application | è¯»å– WCS è®¾å¤‡æ•°æ® |
| `InstanceSyncService` | Application | åŒæ­¥é€»è¾‘ |
| `SyncController` | Server | API æŽ¥å£ |
## é…ç½®æ–°å¢ž
`appsettings.json` æ·»åŠ ï¼š
```json
{
  "WcsDb": {
    "Enabled": true,
    "ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWCS_ShanMei;User ID=sa;Password=${WCS_DB_PASSWORD};...",
    "DbType": 2
  }
}
```
Note: `DbType` uses integer values (SqlServer=2, MySql=1). `${WCS_DB_PASSWORD}` is an environment variable placeholder.
## åè®®æ¨¡æ¿æ˜ å°„
| Dt_DeviceProtocol å­—段 | ProtocolTemplate å­—段 |
|----------------------|----------------------|
| `DeviceChildCode` | `fieldKey` |
| `DeviceProDataBlock` | `dbNumber` |
| `DeviceProOffset` | `offset` |
| `DeviceProDataType` | `dataType`(需转换映射) |
| `DeviceProDataLength` | `length` |
| å›ºå®šå€¼ `1` | `bit` |
| `Bidirectional` (2) | `direction` |
### æ•°æ®ç±»åž‹æ˜ å°„
ProtocolDataType æžšä¸¾å€¼ï¼šByte=0, Int=1, DInt=2, String=3, Bool=4
| æ•°æ®åº“ DeviceProDataType | ProtocolDataType |
|------------------------|-----------------|
| `Bit` | `Bool` (4) |
| `Byte` | `Byte` (0) |
| `Int`, `Word` | `Int` (1) |
| `DInt` | `DInt` (2) |
| `String`, `String8`, `String16` | `String` (3) |
## å®žä¾‹é…ç½®é»˜è®¤å€¼
```csharp
new MemoryRegionConfig
{
    MRegionSize = 1024,
    DBBlockCount = 1,
    DBBlockNumbers = new List<int> { 50 },
    DBBlockSize = 65536,
    IRegionSize = 256,
    QRegionSize = 256,
    TRegionCount = 64,
    CRegionCount = 64
}
```
## åè®®æ¨¡æ¿ ID æ ¼å¼
`protocol-{DeviceCode}`
每个设备(DeviceCode)生成一个协议模板,包含该设备所有 DeviceChildCode çš„协议字段。
## API æŽ¥å£
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| POST | `/api/Sync/SyncInstances` | æ‰‹åŠ¨è§¦å‘åŒæ­¥ |
| GET | `/api/Sync/LastSyncTime` | èŽ·å–ä¸Šæ¬¡åŒæ­¥æ—¶é—´ |
## åŒæ­¥é€»è¾‘
1. è¯»å–数据库中所有 `DevicePlcType = 'SiemensS7'` çš„设备
2. å¯¹æ¯ä¸ªè®¾å¤‡ï¼Œåˆ›å»º `InstanceConfig`(ID = DeviceCode,端口 = DevicePort)
3. å¯¹æ¯ä¸ªè®¾å¤‡ï¼Œåˆå¹¶å…¶æ‰€æœ‰ `Dt_DeviceProtocol` è®°å½•生成协议模板
4. å¯¹æ¯”现有实例:
   - **新增的**:创建实例 + ä¿å­˜åè®®æ¨¡æ¿
   - **已存在的**:更新实例配置(可选)
   - **数据库已删除的**:从内存移除,保留文件配置
5. åŒæ­¥åŽå®žä¾‹çŠ¶æ€é»˜è®¤ä¸º **Stopped**(不自动启动)
## æ–‡ä»¶å˜æ›´
### æ–°å¢žæ–‡ä»¶
- `WIDESEAWCS_S7Simulator.Application/WcsDbOptions.cs`
- `WIDESEAWCS_S7Simulator.Application/DatabaseDeviceService.cs`
- `WIDESEAWCS_S7Simulator.Application/InstanceSyncService.cs`
- `WIDESEAWCS_S7Simulator.Server/Controllers/SyncController.cs`
### ä¿®æ”¹æ–‡ä»¶
- `WIDESEAWCS_S7Simulator.Server/appsettings.json` - æ·»åŠ  WcsDb é…ç½®
- `WIDESEAWCS_S7Simulator.Server/Program.cs` - æ³¨å†ŒæœåŠ¡ã€å¯åŠ¨æ—¶åŒæ­¥