kazelee
2 天以前 f8d23dcf8d6501482db1a5180325194232afe96c
封装部分业务代码,继续优化日志打印流程
45个文件已修改
1818 ■■■■ 已修改文件
App_Start/Startup.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Program.cs 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Properties/AssemblyInfo.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/AgvController.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/ApiHelper.cs 477 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/ApiModel.cs 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/DebugController.cs 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/MoboxController.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/config.comment.json 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
core/Monitor.cs 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
core/WCSCore.cs 135 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/ModbusFactory.cs 154 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/ModbusHelper.cs 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/OpcUaHelper.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/ProductionLineDevice.cs 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/S7Helper.cs 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/TcpClient.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/TcpClientHelper.cs 83 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/TcpServer.cs 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dispatch/GZRobot.cs 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dispatch/HanAo.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dispatch/HostToAGV.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dispatch/NDCApi.cs 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/BaseModel.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_CAR_IN.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_CG_Detail.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_Container.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_Location.cs 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_Outbound_Detail.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_Outbound_Order.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_Outbound_Plan.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_Record_Table.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_RelocationList_Detail.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_Relocation_List.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_SpotCheck_Detail.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_Spot_Check.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_Task.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
process/DeviceProcess.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
process/TaskProcess.cs 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
swagger.js 98 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
util/HttpHelper.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
util/LogHelper.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
util/SqlHelper.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/LocationHelper.cs 52 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/WCSHelper.cs 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
App_Start/Startup.cs
@@ -14,7 +14,7 @@
namespace HH.WCS.Mobox3.DSZSH {
    public class Startup {
        public void Configuration(IAppBuilder app) {
            // 有关如何配置应用程序的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkID=316888
            // 有关如何配置应用程序的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkID=316888
            HttpConfiguration config = new HttpConfiguration();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
Program.cs
@@ -68,7 +68,7 @@
        }
        
        /// <summary>
        /// 开启TCP协议通讯,服务端
        /// 开启TCP协议通讯,服务端
        /// </summary>
        private static void StartTcp()
        {
@@ -123,16 +123,16 @@
                tasks.Add(GetTask(WCSCore.Dispatch));
                // 测试:托盘下线
                // 测试:托盘下线
                //tasks.Add(GetTask(Monitor.CheckInbound));
                // 轮询:出库单状态
                // 轮询:出库单状态
                tasks.Add(GetTask(Monitor.CheckOutboundOrder));
                // 轮询:抽检单状态
                // 轮询:抽检单状态
                tasks.Add(GetTask(Monitor.CheckCheckOrder));
                // 轮询:移库单状态
                // 轮询:移库单状态
                tasks.Add(GetTask(Monitor.CheckShiftOrder));
                Task.WaitAll(tasks.ToArray());
@@ -151,7 +151,7 @@
                        }
                        catch (Exception ex)
                        {
                            LogHelper.Error(ex.Message, ex);
                            LogHelper.InfoEx(ex);
                        }
                        Thread.Sleep(intervalMs);
                    }
Properties/AssemblyInfo.cs
@@ -19,7 +19,7 @@
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("8e589c0d-7d65-474d-8ced-e34e087126a1")]
// 程序集的版本信息由下列四个值组成: 
@@ -29,8 +29,8 @@
//      生成号
//      修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
api/AgvController.cs
@@ -16,7 +16,7 @@
namespace HH.WCS.Mobox3.DSZSH.api {
    /// <summary>
    /// 设备信息上报(HostToAGV上报、杭奥堆垛机、国自AGV)
    /// 设备信息上报 (HostToAGV上报,杭奥堆垛机,国自AGV)
    /// </summary>
    [RoutePrefix("agv")]
    public class AgvController : ApiController
@@ -29,7 +29,7 @@
        [HttpPost]
        [Route("AGVCallbackState")]
        public ReturnResult AGVCallbackState(AgvTaskState model){
            LogHelper.InfoHostToAGV("AGVCallbackState:NDC任务状态回报", model);
            LogHelper.InfoHostToAGV("AGVCallbackState:NDC任务状态回报", model);
            return WCSCore.OperateAgvTaskStatus(model);
        }
@@ -41,7 +41,7 @@
        [HttpPost]
        [Route("SafetyInteraction")]
        public ReturnResult SafetyInteraction(SafetyInteractionInfo model) {
            LogHelper.InfoHostToAGV("SafetyInteraction:AGV与产线进行安全交互", model);
            LogHelper.InfoHostToAGV("SafetyInteraction:AGV与产线进行安全交互", model);
            return WCSCore.SafetyInteraction(model);
        }
    }
api/ApiHelper.cs
@@ -25,83 +25,84 @@
        public static SimpleResult GoodpackOffline(GoodpackOfflineInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var taskInfo = ETask.M满箱下线入库.Info();
            const string preLog = "API:满箱下线入库:";
            const string preLog = "API:满箱下线入库:";
            const string cntrType = "好运箱";
            try {
                if (model.Num <= 0) {
                    return NewSimpleResult(400, preLog + $"物料数量'{model.Num}'不合法!要求:物料数量>0");
                    return NewSimpleResult(400, preLog + $"物料数量'{model.Num}'不合法!要求:物料数量>0");
                }
                // 检查货品容器表:是否已经存在贴标机传递的待入库物料信息
                // TODO:数量、规格是否也参与比对?
                // 检查货品容器表:是否已经存在贴标机传递的待入库物料信息
                // TODO:数量,规格是否也参与比对?
                var cgDetail = db.Queryable<TN_CG_Detail>().Where(d => d.S_ITEM_CODE == model.ItemCode && d.S_BATCH_NO == model.BatchNo && d.N_ITEM_STATE == 1 && d.S_ITEM_STATE == "待检").First();
                if (cgDetail == null) {
                    return NewSimpleResult(1, preLog + $"没有在[货品明细表]中找到[物料编码='{model.ItemCode}',批次号='{model.BatchNo}']的物料!请检查:PDA扫码物料信息与贴标机传递的信息是否一致!要求:物料状态='待检'");
                    return NewSimpleResult(1, preLog + $"没有在[货品明细表]中找到[物料编码='{model.ItemCode}',批次号='{model.BatchNo}']的物料!请检查:PDA扫码物料信息与贴标机传递的信息是否一致!要求:物料状态='待检'");
                }
                // 查询起点货位:数量=0
                var startLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y").Where(l => l.S_CODE == model.StartLoc && taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).First();
                // 查询起点货位:数量=0
                var startLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && l.S_CODE == model.StartLoc && taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).First();
                if (startLoc == null) {
                    return NewSimpleResult(2, preLog + $"没有找到起点货位'{model.StartLoc}'!要求:锁状态='无',当前容器数量=0,所在库区={LogObject(taskInfo.StartAreas)}");
                    return NewSimpleResult(2, preLog + $"没有找到起点货位'{model.StartLoc}'!要求:锁状态='无',当前容器数量=0,所在库区={LogObject(taskInfo.StartAreas)}");
                }
                // 和满托下线入库的逻辑一致,由于容器移动不会更改绑定信息,所以必须删除旧数据
                var locCntrRelOld = db.Queryable<TN_Loc_Container>()
                    .Where(c => c.S_CNTR_CODE == cgDetail.S_CNTR_CODE).First();
                // 和满托下线入库的逻辑一致,由于容器移动不会更改绑定信息,所以必须删除旧数据
                var old = WCSHelper.GetLocCntrCg(cgDetail.S_CNTR_CODE, skipCgDetail: true);
                // 绑定货位容器,起点货位当前数量=1
                var locCntrRel = new TN_Loc_Container { S_LOC_CODE = startLoc.S_CODE, S_CNTR_CODE = cgDetail.S_CNTR_CODE, S_CNTR_TYPE = cntrType,};
                startLoc.N_CURRENT_NUM = 1;
                // 绑定货位容器,起点货位当前数量=1
                var locCntrRel = WCSHelper.BindLocCntr(ref startLoc, cgDetail.S_CNTR_CODE);
                locCntrRel.S_CNTR_TYPE = cntrType;
                // 查询终点货位
                // Order:按货位层数,从小到大排列
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y")
                    .Where(l => taskInfo.EndAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).OrderBy(l => new { l.N_LAYER }).First();
                // Order:按货位层数,从小到大排列
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && taskInfo.EndAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).OrderBy(l => new { l.N_LAYER }).First();
                if (endLoc == null) {
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0,所在库区={LogObject(taskInfo.EndAreas)}");
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0,所在库区={LogObject(taskInfo.EndAreas)}");
                }
                // 起点终点上锁,创建任务
                // 更新[起点/终点]锁状态,创建任务
                WCSHelper.LockStartLoc(ref startLoc);
                WCSHelper.LockEndLoc(ref endLoc);
                var task = WCSHelper.BuildTask(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                using (var tran = db.Ado.UseTran()) {
                    if (locCntrRelOld != null) {
                        if (db.Deleteable(locCntrRelOld).ExecuteCommand() <= 0 &&
                            db.Updateable<TN_Location>().SetColumns(l => l.N_CURRENT_NUM == 0).Where(l => l.S_CODE == locCntrRelOld.S_LOC_CODE).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            return NewSimpleResult(500, preLog + $"删除旧货位容器关系表失败!货位编码={locCntrRelOld.S_LOC_CODE},容器编码={locCntrRelOld.S_CNTR_CODE}");
                        }
                    // 删除/更新旧[货位/容器/物料]信息
                    if (old.LocCntrRel != null && db.Deleteable(old.LocCntrRel).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"删除[旧货位容器关系]失败!数据:{LogObject(old.LocCntrRel)}");
                    }
                    if (old.Location != null && db.Updateable(old.Location).UpdateColumns(l => new { l.N_CURRENT_NUM, l.T_MODIFY }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[旧货位|当前容器数量]失败!货位='{old.Location.S_CODE}',数量=>{old.Location.N_CURRENT_NUM}");
                    }
                    // 插入新绑定的[货位-容器]表
                    if (db.Insertable(locCntrRel).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"插入[容器货位绑定表]失败!数据:{LogObject(locCntrRel)}");
                        return NewSimpleResult(500, preLog + $"插入[容器货位绑定表]失败!数据:{LogObject(locCntrRel)}");
                    }
                    // 更新[起点/终点]锁状态,创建任务
                    if (db.Updateable(startLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, it.N_CURRENT_NUM, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                    }
                    if (db.Updateable(endLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                    }
                    if (db.Insertable(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    }
                    // 提交数据库更改
                    tran.CommitTran();
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                }
            }
            catch (Exception ex) {
                return NewSimpleResult(-1, preLog + $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewSimpleResult(ex, preLog);
            }
        }
@@ -113,95 +114,86 @@
        public static SimpleResult EmptyInboundPallet(EmptyInboundInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var taskInfo = ETask.K空托入库.Info();
            const string preLog = "API:空托入库:";
            const string preLog = "API:空托入库:";
            const string cntrType = "托盘";
            try {
                // 查询起点货位:数量=0
                var startLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y").Where(l => l.S_CODE == model.StartLoc && taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).First();
                // 查询起点货位:数量=0
                var startLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && l.S_CODE == model.StartLoc && taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).First();
                if (startLoc == null) {
                    return NewSimpleResult(1, preLog + $"没有找到起点货位'{model.StartLoc}'!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.StartAreas)}");
                    return NewSimpleResult(1, preLog + $"没有找到起点货位'{model.StartLoc}'!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.StartAreas)}");
                }
                // 查询容器表:容器类型字段
                // 查询容器表:容器类型字段
                var cntr = db.Queryable<TN_Container>().Where(c => c.S_CODE == model.CntrCode).First();
                if (cntr == null) {
                    return NewSimpleResult(2, preLog + $"容器'{model.CntrCode}'在[容器表]中不存在,请在前台页面中维护!");
                    return NewSimpleResult(2, preLog + $"容器'{model.CntrCode}'在[容器表]中不存在,请在前台页面中维护!");
                }
                if (cntr.S_TYPE != cntrType) {
                    return NewSimpleResult(3, preLog + $"容器'{model.CntrCode}'在[容器表]中的类型为'{cntr.S_TYPE}',与输入的容器类型'{cntrType}'不同!");
                    return NewSimpleResult(3, preLog + $"容器'{model.CntrCode}'在[容器表]中的类型为'{cntr.S_TYPE}',与输入的容器类型'{cntrType}'不同!");
                }
                // 空箱入库时,如果存在旧的绑定数据,删除
                var old = WCSHelper.GetLocCntrCg(model.CntrCode);
                // 空箱入库时,如果存在旧的绑定数据,删除
                var cgDetailOld = db.Queryable<TN_CG_Detail>().Where(d => d.S_CNTR_CODE == model.CntrCode).First();
                var locCntrRelOld = db.Queryable<TN_Loc_Container>().Where(c => c.S_CNTR_CODE == model.CntrCode).First();
                TN_Location locOld = null;
                if (locCntrRelOld != null) {
                    locOld = db.Queryable<TN_Location>().Where(l => l.S_CODE == locCntrRelOld.S_LOC_CODE).First();
                    if (locOld != null) {
                        locOld.N_CURRENT_NUM = 0; // 如果旧货位存在,将旧货位的数量设置为 0
                        locOld.T_MODIFY = DateTime.Now;
                    }
                }
                // 绑定货位容器,起点货位当前数量=1
                var locCntrRel = new TN_Loc_Container { S_LOC_CODE = startLoc.S_CODE, S_CNTR_CODE = cntr.S_CODE, S_CNTR_TYPE = cntrType };
                startLoc.N_CURRENT_NUM = 1;
                // 绑定货位容器,起点货位当前数量=1
                var locCntrRel = WCSHelper.BindLocCntr(ref startLoc, cntr.S_CODE);
                locCntrRel.S_CNTR_TYPE = cntrType;
                // 查询终点货位
                // Order:层数从低到高、行、列
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y").Where(l => taskInfo.EndAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).OrderBy(l => new { l.N_LAYER, l.N_ROW, l.N_COL }).First();
                // Order:层数从低到高,行,列
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && taskInfo.EndAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).OrderBy(l => new { l.N_LAYER, l.N_ROW, l.N_COL }).First();
                if (endLoc == null) {
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.EndAreas)}");
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.EndAreas)}");
                }
                // 起点终点上锁,创建任务
                // 起点终点上锁,创建任务
                WCSHelper.LockStartLoc(ref startLoc);
                WCSHelper.LockEndLoc(ref endLoc);
                var task = WCSHelper.BuildTask(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                using (var tran = db.Ado.UseTran()) {
                    if (cgDetailOld != null && db.Deleteable(cgDetailOld).ExecuteCommand() <= 0) {
                    if (old.CgDetail != null && db.Deleteable(old.CgDetail).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"删除[旧物料信息]失败!数据:{LogObject(cgDetailOld)}");
                        return NewSimpleResult(500, preLog + $"删除[旧物料信息]失败!数据:{LogObject(old.CgDetail)}");
                    }
                    if (locCntrRelOld != null && db.Deleteable(locCntrRelOld).ExecuteCommand() <= 0) {
                    if (old.LocCntrRel != null && db.Deleteable(old.LocCntrRel).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"删除[旧货位容器关系]失败!数据:{LogObject(locCntrRelOld)}");
                        return NewSimpleResult(500, preLog + $"删除[旧货位容器关系]失败!数据:{LogObject(old.LocCntrRel)}");
                    }
                    if (locOld != null && db.Updateable(locOld).UpdateColumns(l => new { l.N_CURRENT_NUM, l.T_MODIFY }).ExecuteCommand() <= 0) {
                    if (old.Location != null && db.Updateable(old.Location).UpdateColumns(l => new { l.N_CURRENT_NUM, l.T_MODIFY }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[旧货位|当前容器数量]失败!货位='{locOld.S_CODE}',数量=>{locOld.N_CURRENT_NUM}");
                        return NewSimpleResult(500, preLog + $"更新[旧货位|当前容器数量]失败!货位='{old.Location.S_CODE}',数量=>{old.Location.N_CURRENT_NUM}");
                    }
                    if (db.Insertable(locCntrRel).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"插入[货位容器绑定表]失败!数据:{LogObject(locCntrRel)}");
                        return NewSimpleResult(500, preLog + $"插入[货位容器绑定表]失败!数据:{LogObject(locCntrRel)}");
                    }
                    // 更新[起点/终点]锁状态,创建任务
                    if (db.Updateable(startLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, it.N_CURRENT_NUM, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                    }
                    if (db.Updateable(endLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                    }
                    if (db.Insertable(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    }
                    // 提交数据库更改
                    tran.CommitTran();
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                }
            }
            catch (Exception ex) {
                return NewSimpleResult(-1, preLog + $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewSimpleResult(ex, preLog);
            }
        }
@@ -212,46 +204,37 @@
        public static SimpleResult EmptyInboundGoodpack(EmptyInboundInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var taskInfo = ETask.K空箱入库.Info();
            const string preLog = "API:空箱入库:";
            const string preLog = "API:空箱入库:";
            const string cntrType = "好运箱";
            try {
                // 查询起点货位:数量=0
                // 查询起点货位:数量=0
                var startLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y").Where(l => l.S_CODE == model.StartLoc && taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).First();
                if (startLoc == null) {
                    return NewSimpleResult(1, preLog + $"没有找到起点货位'{model.StartLoc}'!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.StartAreas)}");
                    return NewSimpleResult(1, preLog + $"没有找到起点货位'{model.StartLoc}'!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.StartAreas)}");
                }
                // 查询容器表:容器类型字段
                // 查询容器表:容器类型字段
                var cntr = db.Queryable<TN_Container>().Where(c => c.S_CODE == model.CntrCode).First();
                if (cntr == null) {
                    return NewSimpleResult(2, preLog + $"容器'{model.CntrCode}'在[容器表]中不存在,请在前台页面中维护!");
                    return NewSimpleResult(1, $"容器'{model.CntrCode}'在[容器表]中不存在,请在前台页面中维护!");
                }
                if (cntr.S_TYPE != cntrType) {
                    return NewSimpleResult(3, preLog + $"容器'{model.CntrCode}'在[容器表]中的类型为'{cntr.S_TYPE}',与输入的容器类型'{cntrType}'不同!");
                    return NewSimpleResult(2, preLog + $"容器'{model.CntrCode}'在[容器表]中的类型是'{cntr.S_TYPE},不是'{cntrType}'!");
                }
                // 空箱入库时,如果存在旧的绑定数据,删除
                var cgDetailOld = db.Queryable<TN_CG_Detail>().Where(d => d.S_CNTR_CODE == model.CntrCode).First();
                var locCntrRelOld = db.Queryable<TN_Loc_Container>().Where(c => c.S_CNTR_CODE == model.CntrCode).First();
                TN_Location locOld = null;
                if (locCntrRelOld != null) {
                    locOld = db.Queryable<TN_Location>().Where(l => l.S_CODE == locCntrRelOld.S_LOC_CODE).First();
                    if (locOld != null) {
                        locOld.N_CURRENT_NUM = 0; // 如果旧货位存在,将旧货位的数量设置为 0
                        locOld.T_MODIFY = DateTime.Now;
                    }
                }
                // 空箱入库时,如果存在旧的绑定数据,删除
                var old = WCSHelper.GetLocCntrCg(model.CntrCode);
                // 绑定货位容器,起点货位当前数量=1
                var locCntrRel = new TN_Loc_Container { S_LOC_CODE = startLoc.S_CODE, S_CNTR_CODE = cntr.S_CODE, S_CNTR_TYPE = cntrType };
                startLoc.N_CURRENT_NUM = 1;
                // 绑定货位容器,起点货位当前数量=1
                var locCntrRel = WCSHelper.BindLocCntr(ref startLoc, model.CntrCode);
                locCntrRel.S_CNTR_TYPE = cntrType;
                // 查询终点货位
                // Order:层数从低到高、行、列
                // Order:层数从低到高,行,列
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y").Where(l => taskInfo.EndAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).OrderBy(l => new { l.N_LAYER, l.N_ROW, l.N_COL }).First();
                if (endLoc == null) {
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.EndAreas)}");
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.EndAreas)}");
                }
                WCSHelper.LockStartLoc(ref startLoc);
@@ -259,47 +242,45 @@
                var task = WCSHelper.BuildTask(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                using (var tran = db.Ado.UseTran()) {
                    if (cgDetailOld != null && db.Deleteable(cgDetailOld).ExecuteCommand() <= 0) {
                    if (old.CgDetail != null && db.Deleteable(old.CgDetail).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"删除[旧物料信息]失败!数据:{LogObject(cgDetailOld)}");
                        return NewSimpleResult(500, preLog + $"删除[旧物料信息]失败!数据:{LogObject(old.CgDetail)}");
                    }
                    if (locCntrRelOld != null && db.Deleteable(locCntrRelOld).ExecuteCommand() <= 0) {
                    if (old.LocCntrRel != null && db.Deleteable(old.LocCntrRel).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"删除[旧货位容器关系]失败!" + LogObject(locCntrRelOld));
                        return NewSimpleResult(500, preLog + $"删除[旧货位容器关系]失败!数据:{LogObject(old.LocCntrRel)}");
                    }
                    if (locOld != null && db.Updateable(locOld).UpdateColumns(l => new { l.N_CURRENT_NUM, l.T_MODIFY }).ExecuteCommand() <= 0) {
                    if (old.Location != null && db.Updateable(old.Location).UpdateColumns(l => new { l.N_CURRENT_NUM, l.T_MODIFY }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[旧货位|容器数量]失败!" + LogObject(locOld));
                        return NewSimpleResult(500, preLog + $"更新[旧货位|当前容器数量]失败!货位='{old.Location.S_CODE}',数量=>{old.Location.N_CURRENT_NUM}");
                    }
                    if (db.Insertable(locCntrRel).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"插入[货位容器关系]失败:" + LogObject(locCntrRel));
                        return NewSimpleResult(500, preLog + $"插入[货位容器关系]失败:" + LogObject(locCntrRel));
                    }
                    // 更新[起点/终点]锁状态,创建任务
                    if (db.Updateable(startLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, it.N_CURRENT_NUM, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                    }
                    if (db.Updateable(endLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                    }
                    if (db.Insertable(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    }
                    // 提交数据库更改
                    tran.CommitTran();
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                }
            }
            catch (Exception ex) {
                return NewSimpleResult(-1, preLog + $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewSimpleResult(ex, preLog);
            }
        }
@@ -311,41 +292,41 @@
        public static SimpleResult EmptyOnlinePallet(EmptyOnlinePalletInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var taskInfo = ETask.K空托上线出库.Info();
            const string preLog = "API:空托上线出库:";
            const string preLog = "API:空托上线出库:";
            const string cntrType = "托盘";
            try {
                // 查询容器表:容器类型字段
                // 查询容器表:容器类型字段
                var cntr = db.Queryable<TN_Container>().Where(c => c.S_CODE == model.CntId).First();
                if (cntr == null) {
                    return NewSimpleResult(1, $"容器'{model.CntId}'在[容器表]中不存在,请在前台页面中维护!");
                    return NewSimpleResult(1, $"容器'{model.CntId}'在[容器表]中不存在,请在前台页面中维护!");
                }
                if (cntr.S_TYPE != cntrType) {
                    return NewSimpleResult(2, preLog + $"容器'{model.CntId}'在[容器表]中的类型是'{cntr.S_TYPE},不是'{cntrType}'!");
                    return NewSimpleResult(2, preLog + $"容器'{model.CntId}'在[容器表]中的类型是'{cntr.S_TYPE},不是'{cntrType}'!");
                }
                var needUpdateContainer = false;
                if (string.IsNullOrEmpty(cntr.S_SPEC)) {
                    // TEMP 目前流程:如果容器表中规格(物料编码)为空,根据流程的物料信息写入
                    // 待定:后面可能会更改流程,或者用其他信息(如物料类型/规格)作为容器的规格
                    // TEMP 目前流程:如果容器表中规格 (物料编码) 为空,根据流程的物料信息写入
                    // 待定:后面可能会更改流程,或者用其他信息 (如物料类型/规格) 作为容器的规格
                    needUpdateContainer = true;
                    cntr.S_SPEC = model.ItemCode;
                    LogHelper.Info($"容器'{model.CntId}'在[容器表]中[规格(物料编码)]为空,将物料编码'{model.ItemCode}'写入容器的规格");
                    LogHelper.Info($"容器'{model.CntId}'在[容器表]中[规格(物料编码)]为空,将物料编码'{model.ItemCode}'写入容器的规格");
                }
                else if (cntr.S_SPEC != model.ItemCode) {
                    return NewSimpleResult(3, $"容器'{model.CntId}'已经与物料类型'{cntr.S_SPEC}'绑定,无法用于装载物料'{model.ItemCode}'!");
                    return NewSimpleResult(3, $"容器'{model.CntId}'已经与物料类型'{cntr.S_SPEC}'绑定,无法用于装载物料'{model.ItemCode}'!");
                }
                var startLoc = db.Queryable<TN_Location>().LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE).Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y").Where((l, c) => taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 1 && c.S_CNTR_CODE == model.CntId && c.S_CNTR_TYPE == cntrType).First();
                if (startLoc == null) {
                    return NewSimpleResult(3, preLog + $"没有找到合适的起点货位!要求:锁状态='无';当前容器数量=1;所在库区={LogObject(taskInfo.StartAreas)},绑定容器编码='{model.CntId}',绑定容器类型='{cntrType}'");
                    return NewSimpleResult(3, preLog + $"没有找到合适的起点货位!要求:锁状态='无';当前容器数量=1;所在库区={LogObject(taskInfo.StartAreas)},绑定容器编码='{model.CntId}',绑定容器类型='{cntrType}'");
                }
                // 查询终点货位
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y").Where(l => taskInfo.EndAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).First();
                if (endLoc == null) {
                    return NewSimpleResult(5, preLog + $"没有找到合适的终点货位!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.EndAreas)}");
                    return NewSimpleResult(5, preLog + $"没有找到合适的终点货位!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.EndAreas)}");
                }
                WCSHelper.LockStartLoc(ref startLoc);
@@ -357,30 +338,30 @@
                using (var tran = db.Ado.UseTran()) {
                    if (needUpdateContainer && db.Updateable(cntr).UpdateColumns(c => new { c.S_SPEC, c.S_SOURCE, c.T_MODIFY }).ExecuteCommand() <= 0) {
                        return NewSimpleResult(500, preLog + $"更新[容器表]失败!数据:{LogObject(cntr)}");
                        return NewSimpleResult(500, preLog + $"更新[容器表]失败!数据:{LogObject(cntr)}");
                    }
                    // 更新[起点/终点]锁状态,创建任务
                    if (db.Updateable(startLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, it.N_CURRENT_NUM, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                    }
                    if (db.Updateable(endLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                    }
                    if (db.Insertable(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    }
                    // 提交数据库更改
                    tran.CommitTran();
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                }
            }
            catch (Exception ex) {
                return NewSimpleResult(-1, preLog + $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewSimpleResult(ex, preLog);
            }
        }
@@ -392,30 +373,27 @@
        public static SimpleResult EmptyOnlineGoodpack(EmptyOnlineGoodpackInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var taskInfo = ETask.K空箱上线出库.Info();
            const string preLog = "API:空箱上线出库:";
            const string preLog = "API:空箱上线出库:";
            const string cntrType = "好运箱";
            try {
                var cntr = db.Queryable<TN_Container>()
                    .Where(c => c.S_CODE == model.CntId)
                    .First();
                var cntr = db.Queryable<TN_Container>().Where(c => c.S_CODE == model.CntId).First();
                if (cntr == null) {
                    return NewSimpleResult(1, preLog + $"容器'{model.CntId}'在[容器表]中不存在,请在前台页面中维护!");
                    return NewSimpleResult(1, preLog + $"容器'{model.CntId}'在[容器表]中不存在,请在前台页面中维护!");
                }
                if (cntr.S_TYPE != cntrType) {
                    return NewSimpleResult(2, preLog + $"容器'{model.CntId}'在[容器表]中的类型='{cntr.S_TYPE}',不是'{cntrType}'!");
                    return NewSimpleResult(2, preLog + $"容器'{model.CntId}'在[容器表]中的类型='{cntr.S_TYPE}',不是'{cntrType}'!");
                }
                var startLoc = db.Queryable<TN_Location>().LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE).Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y").Where((l, c) => taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 1 && c.S_CNTR_CODE == model.CntId && c.S_CNTR_TYPE == cntrType).First();
                if (startLoc == null) {
                    return NewSimpleResult(2, preLog + $"没有找到合适的起点货位!要求:锁状态='无',当前容器数量=1,所在库区={LogObject(taskInfo.StartAreas)},绑定容器编码='{model.CntId}',绑定容器类型='{cntrType}'");
                    return NewSimpleResult(2, preLog + $"没有找到合适的起点货位!要求:锁状态='无',当前容器数量=1,所在库区={LogObject(taskInfo.StartAreas)},绑定容器编码='{model.CntId}',绑定容器类型='{cntrType}'");
                }
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y")
                    .Where(l => taskInfo.EndAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).First();
                if (endLoc == null) {
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0,所在库区={LogObject(taskInfo.EndAreas)}");
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0,所在库区={LogObject(taskInfo.EndAreas)}");
                }
                WCSHelper.LockStartLoc(ref startLoc);
@@ -423,27 +401,27 @@
                var task = WCSHelper.BuildTask(startLoc, endLoc, model.CntId, taskInfo.TaskName);
                
                using (var tran = db.Ado.UseTran()) {
                    // 更新[起点/终点]锁状态,创建任务
                    if (db.Updateable(startLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, it.N_CURRENT_NUM, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                    }
                    if (db.Updateable(endLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                    }
                    if (db.Insertable(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    }
                    // 提交数据库更改
                    tran.CommitTran();
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                }
            }
            catch (Exception ex) {
                return NewSimpleResult(-1, preLog + $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewSimpleResult(ex, preLog);
            }
        }
@@ -455,32 +433,29 @@
        public static SimpleResult QualifiedBack(QualifiedBackInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var taskInfo = ETask.C抽检合格回库.Info();
            const string preLog = "API:抽检合格回库";
            const string preLog = "API:抽检合格回库";
            try {
                var cgDetail = db.Queryable<TN_CG_Detail>()
                    .Where(d => d.S_ITEM_CODE == model.ItemCode && d.S_CNTR_CODE == model.CntrCode).First();
                if (cgDetail == null) {
                    return NewSimpleResult(2, preLog + "没有找到待回库的抽检物料:" + LogObject(model));
                    return NewSimpleResult(2, preLog + "没有找到待回库的抽检物料:" + LogObject(model));
                }
                var locCntrRel = db.Queryable<TN_Loc_Container>()
                    .Where(c => c.S_CNTR_CODE == cgDetail.S_CNTR_CODE).First();
                var locCntrRel = db.Queryable<TN_Loc_Container>().Where(c => c.S_CNTR_CODE == cgDetail.S_CNTR_CODE).First();
                if (locCntrRel == null) {
                    return NewSimpleResult(3, preLog + $"容器{model.CntrCode}在货位容器关系表中不存在");
                }
                var startLoc = db.Queryable<TN_Location>()
                    .Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y").Where(l => l.S_CODE == locCntrRel.S_LOC_CODE && taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 1).First();
                var startLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && l.S_CODE == locCntrRel.S_LOC_CODE && taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 1).First();
                if (startLoc == null) {
                    return NewSimpleResult(4, preLog + $"没有找到合适的起点货位!要求:锁状态='无',当前容器数量=1,所在库区={LogObject(taskInfo.StartAreas)}");
                    return NewSimpleResult(4, preLog + $"没有找到合适的起点货位!要求:锁状态='无',当前容器数量=1,所在库区={LogObject(taskInfo.StartAreas)}");
                }
                var endAreas = locCntrRel.S_CNTR_CODE == "托盘" ? taskInfo.EndAreas_Pallet : taskInfo.EndAreas_Goodpack;
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y")
                    .Where(l => endAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).First();
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && endAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).First();
                if (endLoc == null) {
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0,所在库区={LogObject(endAreas)}");
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0,所在库区={LogObject(endAreas)}");
                }
                cgDetail.N_ITEM_STATE = 0;
@@ -496,30 +471,30 @@
                using (var tran = db.Ado.UseTran()) {
                    if (db.Updateable(cgDetail).UpdateColumns(it => new { it.N_ITEM_STATE, it.S_ITEM_STATE, it.T_MODIFY }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[物料明细表]失败!物料号='{cgDetail.S_ITEM_CODE}',物料状态=>'合格'");
                        return NewSimpleResult(500, preLog + $"更新[物料明细表]失败!物料号='{cgDetail.S_ITEM_CODE}',物料状态=>'合格'");
                    }
                    // 更新[起点/终点]锁状态,创建任务
                    if (db.Updateable(startLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, it.N_CURRENT_NUM, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                    }
                    if (db.Updateable(endLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                    }
                    if (db.Insertable(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    }
                    // 提交数据库更改
                    tran.CommitTran();
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                }
            }
            catch (Exception ex) {
                return NewSimpleResult(-1, preLog + $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewSimpleResult(ex, preLog);
            }
        }
@@ -531,34 +506,33 @@
        public static SimpleResult UnqualifiedShift(UnqualifiedShiftInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var taskInfo = ETask.C抽检不合格移库.Info();
            const string preLog = "API:抽检不合格移库:";
            const string preLog = "API:抽检不合格移库:";
            try {
                if (!taskInfo.EndAreas.Contains(model.EndArea)) {
                    return NewSimpleResult(1, preLog + $"终点库区'{model.EndArea}'不满足条件!需要:货区={LogObject(taskInfo.EndAreas)}");
                    return NewSimpleResult(1, preLog + $"终点库区'{model.EndArea}'不满足条件!需要:货区={LogObject(taskInfo.EndAreas)}");
                }
                var cgDetail = db.Queryable<TN_CG_Detail>()
                    .Where(d => d.S_ITEM_CODE == model.ItemCode && d.S_CNTR_CODE == model.CntrCode).First();
                if (cgDetail == null) {
                    return NewSimpleResult(2, preLog + $"没有在[物料明细表]中找到物料!要求:物料编码='{model.ItemCode}',容器编码='{model.CntrCode}'");
                    return NewSimpleResult(2, preLog + $"没有在[物料明细表]中找到物料!要求:物料编码='{model.ItemCode}',容器编码='{model.CntrCode}'");
                }
                var locCntrRel = db.Queryable<TN_Loc_Container>().Where(c => c.S_CNTR_CODE == cgDetail.S_CNTR_CODE).First();
                if (locCntrRel == null) {
                    return NewSimpleResult(3, preLog + $"在[货位容器关系表]中没有找到容器'{model.CntrCode}'!");
                    return NewSimpleResult(3, preLog + $"在[货位容器关系表]中没有找到容器'{model.CntrCode}'!");
                }
                // 查询起点货位:数量=1
                var startLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y").Where(l => l.S_CODE == locCntrRel.S_LOC_CODE && taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 1).First();
                // 查询起点货位:数量=1
                var startLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && l.S_CODE == locCntrRel.S_LOC_CODE && taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 1).First();
                if (startLoc == null) {
                    return NewSimpleResult(1, preLog + $"没有找到起点货位'{locCntrRel.S_LOC_CODE}'!要求:锁状态='无';当前容器数量=1;所在库区={LogObject(taskInfo.StartAreas)}");
                    return NewSimpleResult(1, preLog + $"没有找到起点货位'{locCntrRel.S_LOC_CODE}'!要求:锁状态='无';当前容器数量=1;所在库区={LogObject(taskInfo.StartAreas)}");
                }
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y")
                    .Where(l => l.S_AREA_CODE == model.EndArea && l.N_CURRENT_NUM == 0).First();
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && l.S_AREA_CODE == model.EndArea && l.N_CURRENT_NUM == 0).First();
                if (endLoc == null) {
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0,所在库区='{model.EndArea}'");
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0,所在库区='{model.EndArea}'");
                }
                cgDetail.N_ITEM_STATE = 2;
@@ -571,30 +545,30 @@
                using (var tran = db.Ado.UseTran()) {
                    if (db.Updateable(cgDetail).UpdateColumns(it => new { it.N_ITEM_STATE, it.S_ITEM_STATE }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[货品明细表]失败!物料号='{cgDetail}',物料状态=>'{cgDetail.S_ITEM_STATE}'");
                        return NewSimpleResult(500, preLog + $"更新[货品明细表]失败!物料号='{cgDetail}',物料状态=>'{cgDetail.S_ITEM_STATE}'");
                    }
                    // 更新[起点/终点]锁状态,创建任务
                    if (db.Updateable(startLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, it.N_CURRENT_NUM, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                    }
                    if (db.Updateable(endLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                    }
                    if (db.Insertable(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    }
                    // 提交数据库更改
                    tran.CommitTran();
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                }
            }
            catch (Exception ex) {
                return NewSimpleResult(-1, preLog + $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewSimpleResult(ex, preLog);
            }
        }
@@ -606,13 +580,12 @@
        public static SimpleResult RestBack(RestBackInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var taskInfo = ETask.W尾料回库.Info();
            const string preLog = "API:尾料回库:";
            const string preLog = "API:尾料回库:";
            
            try {
                var startLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y")
                    .Where(l => l.S_CODE == model.StartLoc && l.N_CURRENT_NUM == 1).First();
                var startLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && l.S_CODE == model.StartLoc && l.N_CURRENT_NUM == 1).First();
                if (startLoc == null) {
                    return NewSimpleResult(2, $"没有找到起点货位'{model.StartLoc}'!要求:锁状态='无';当前容器数量=1");
                    return NewSimpleResult(2, $"没有找到起点货位'{model.StartLoc}'!要求:锁状态='无';当前容器数量=1");
                }
                var locCntrRel = db.Queryable<TN_Loc_Container>().Where(c => c.S_LOC_CODE == model.StartLoc).First();
@@ -621,10 +594,9 @@
                }
                var endAreas = locCntrRel.S_CNTR_CODE == "托盘" ? taskInfo.EndAreas_Pallet : taskInfo.EndAreas_Goodpack;
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y")
                    .Where(l => endAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).First();
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && endAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).First();
                if (endLoc == null) {
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0,所在库区={LogObject(endAreas)}");
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0,所在库区={LogObject(endAreas)}");
                }
                var cntId = locCntrRel.S_CNTR_CODE;
@@ -636,42 +608,42 @@
                using (var tran = db.Ado.UseTran()) {
                    //if (db.Insertable(locCntrRel).ExecuteCommand() <= 0) {
                    //    tran.RollbackTran();
                    //    return NewSimpleResult(500, preLog + $"插入[容器货位绑定表]失败!数据:{LogObject(locCntrRel)}");
                    //    return NewSimpleResult(500, preLog + $"插入[容器货位绑定表]失败!数据:{LogObject(locCntrRel)}");
                    //}
                    // 更新[起点/终点]锁状态,创建任务
                    if (db.Updateable(startLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, it.N_CURRENT_NUM, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                        return NewSimpleResult(500, preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                    }
                    if (db.Updateable(endLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                        return NewSimpleResult(500, preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                    }
                    if (db.Insertable(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                        return NewSimpleResult(500, preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    }
                    // 提交数据库更改
                    tran.CommitTran();
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    return NewSimpleResult(0, preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                }
            }
            catch (Exception ex) {
                return NewSimpleResult(-1, preLog + $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewSimpleResult(ex, preLog);
            }
        }
        /// <summary>
        /// 成品胶出库(PDA) 待定,暂时不需要此功能
        /// 成品胶出库(PDA) 待定,暂时不需要此功能
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static SimpleResult FinishedOutbound(FinishedOutboundInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var taskInfo = ETask.C成品胶出库.Info();
            const string preLog = "API:成品胶出库:";
            const string preLog = "API:成品胶出库:";
            try {
                var orderNo = GenerateOrderNo("出库单号", "CKD");
@@ -706,12 +678,12 @@
                using (var tran = db.Ado.UseTran()) {
                    if (db.Insertable(order).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(2, preLog + "生成出库单失败:" + LogObject(order));
                        return NewSimpleResult(2, preLog + "生成出库单失败:" + LogObject(order));
                    }
                    if (db.Insertable(detailList).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(4, preLog + "生成出库单明细失败:" + LogObject(detailList));
                        return NewSimpleResult(4, preLog + "生成出库单明细失败:" + LogObject(detailList));
                    }
                    tran.CommitTran();
@@ -720,7 +692,7 @@
                return NewSimpleResult(0, preLog + "生成出库单成功");
            }
            catch (Exception ex) {
                return NewSimpleResult(-1, preLog + $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewSimpleResult(ex, preLog);
            }
        }
@@ -731,7 +703,7 @@
        public static SimpleResult FinishedOutboundForce(FinishedOutboundInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var taskInfo = ETask.C成品胶出库.Info();
            const string preLog = "API:成品胶出库:";
            const string preLog = "API:成品胶出库:";
            try {
                var orderNo = GenerateOrderNo("出库单号", "CKD");
@@ -766,12 +738,12 @@
                using (var tran = db.Ado.UseTran()) {
                    if (db.Insertable(order).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(2, preLog + "生成出库单失败:" + LogObject(order));
                        return NewSimpleResult(2, preLog + "生成出库单失败:" + LogObject(order));
                    }
                    if (db.Insertable(detailList).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewSimpleResult(4, preLog + "生成出库单明细失败:" + LogObject(detailList));
                        return NewSimpleResult(4, preLog + "生成出库单明细失败:" + LogObject(detailList));
                    }
                    tran.CommitTran();
@@ -780,7 +752,7 @@
                return NewSimpleResult(0, preLog + "生成出库单成功");
            }
            catch (Exception ex) {
                return NewSimpleResult(-1, preLog + $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewSimpleResult(ex, preLog);
            }
        }
@@ -799,13 +771,13 @@
                    return result;
                }
                // NOTE 根据总量选detail时,是否需要考虑货位的高低?
                // NOTE 根据总量选detail时,是否需要考虑货位的高低?
                var sortedMaterials = new List<TN_CG_Detail>();
                var cntrTypeList = new List<string>();
                if (cntrType == "") {
                    cntrTypeList = new List<string> { "托盘", "好运箱" }; // 不指定托盘类型,二者均可
                    cntrTypeList = new List<string> { "托盘", "好运箱" }; // 不指定托盘类型,二者均可
                }
                else {
                    cntrTypeList.Add(cntrType);
@@ -863,94 +835,94 @@
        }
        /// <summary>
        /// 博实物料信息下发同步(只针对好运箱)
        /// 博实物料信息下发同步 (只针对好运箱)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static MesResult CgInfoSync(CgInfoSyncInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            const string preLog = "API:博实下发物料信息:";
            const string preLog = "API:博实下发物料信息:";
            const string cntrType = "好运箱";
            try {
                if (string.IsNullOrEmpty(model.ItemCode)) {
                    return NewMesResult(400, preLog + $"物料编码'{model.ItemCode}'不能为空!");
                    return NewMesResult(400, preLog + $"物料编码'{model.ItemCode}'不能为空!");
                }
                if (string.IsNullOrEmpty(model.CntrCode)) {
                    return NewMesResult(400, preLog + $"容器编码'{model.CntrCode}'不能为空!");
                    return NewMesResult(400, preLog + $"容器编码'{model.CntrCode}'不能为空!");
                }
                if (string.IsNullOrEmpty(model.BatchNo)) {
                    return NewMesResult(400, preLog + $"批次号'{model.BatchNo}'不能为空!");
                    return NewMesResult(400, preLog + $"批次号'{model.BatchNo}'不能为空!");
                }
                if (model.ItemNum <= 0) {
                    return NewMesResult(400, preLog + $"物料数量'{model.ItemNum}'不合法!要求:物料数量>0");
                    return NewMesResult(400, preLog + $"物料数量'{model.ItemNum}'不合法!要求:物料数量>0");
                }
                // TEMP 目前流程:对博实下发的信息也进行检查,未找到就报错,后面有需求再更改
                // TEMP 目前流程:对博实下发的信息也进行检查,未找到就报错,后面有需求再更改
                var cntr = db.Queryable<TN_Container>()
                    .Where(c => c.S_CODE == model.CntrCode) // 对于前台程序而言,S_CODE就是主键,维护时必定唯一
                    .Where(c => c.S_CODE == model.CntrCode) // 对于前台程序而言,S_CODE就是主键,维护时必定唯一
                    .First();
                if (cntr == null) {
                    return NewMesResult(1, preLog + $"容器'{model.CntrCode}'在[容器表]中不存在,请在前台页面中维护!");
                    return NewMesResult(1, preLog + $"容器'{model.CntrCode}'在[容器表]中不存在,请在前台页面中维护!");
                }
                if (cntr.S_TYPE != cntrType) {
                    return NewMesResult(2, preLog + $"容器'{model.CntrCode}'在[容器表]中的类型为'{cntr.S_TYPE}',与当前容器类型'{cntrType}'不同!");
                    return NewMesResult(2, preLog + $"容器'{model.CntrCode}'在[容器表]中的类型为'{cntr.S_TYPE}',与当前容器类型'{cntrType}'不同!");
                }
                // 将下发的信息先存储到CG表中(此时托盘没有与产线处的货位绑定)
                // 将下发的信息先存储到CG表中 (此时托盘没有与产线处的货位绑定)
                var detail = new TN_CG_Detail {
                    S_ITEM_CODE = model.ItemCode, // NOT NULL
                    S_ITEM_NAME = model.ItemName, 
                    S_CNTR_CODE = model.CntrCode, // NOT NULL 料箱编号(待定,现在暂时设定为博实下发)
                    S_CNTR_CODE = model.CntrCode, // NOT NULL 料箱编号 (待定,现在暂时设定为博实下发)
                    S_BATCH_NO = model.BatchNo,   // NOT NULL
                    S_STANDARD = model.Standard,
                    S_NET_WEIGHT = model.NetWeight,
                    S_QUALITY_GRADE = model.QualityGrade,
                };
                // 产线号的逻辑待定,现在设定为博实下发产线号,看后续需求变更
                // 好运箱起点为产线货位,人工将好运箱从产线上取下,搬运到满好运箱操作区
                // 产线号的逻辑待定,现在设定为博实下发产线号,看后续需求变更
                // 好运箱起点为产线货位,人工将好运箱从产线上取下,搬运到满好运箱操作区
                //var startLocCode = "";
                //if (model.ProdLineId.Trim() == "3") {
                //    startLocCode = "";
                //}
                //if (model.ProdLineId.Trim() != "3" || model.ProdLineId.Trim() != "4") {
                //    info = $"不合法的产线号{model.ProdLineId}:好运箱产线号必须为 3 或 4";
                //    info = $"不合法的产线号{model.ProdLineId}:好运箱产线号必须为 3 或 4";
                //    LogHelper.Info(info);
                //    return NewMesResult(2, info);
                //}
                if (db.Insertable(detail).ExecuteCommand() <= 0) {
                    return NewMesResult(500, preLog + $"插入[物料明细表]失败!数据:{LogObject(detail)}");
                    return NewMesResult(500, preLog + $"插入[物料明细表]失败!数据:{LogObject(detail)}");
                }
                return NewMesResult(500, preLog + $"插入[物料明细表]成功!数据:{LogObject(detail)}");
                return NewMesResult(500, preLog + $"插入[物料明细表]成功!数据:{LogObject(detail)}");
            }
            catch (Exception ex) {
                return NewMesResult(-1, preLog + $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewMesResult(-1, preLog + $"发生了异常:{ex.Message}\n\n{ex.StackTrace}\n");
            }
        }
        public static ErpResult ErpSendOutboundPlan(ErpSendOutboundPlanInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var orderNo = GenerateOrderNo("出库单号", "CKD");
            const string preLog = "API:ERP下发出库计划单:";
            const string preLog = "API:ERP下发出库计划单:";
            try {
                if (model.pzjs <= 0) {
                    return NewErpResult(400, preLog + $"物料数量(pzjs)'{model.pzjs}'不合法!要求:物料数量>0");
                    return NewErpResult(400, preLog + $"物料数量(pzjs)'{model.pzjs}'不合法!要求:物料数量>0");
                }
                var outboundPlan = db.Queryable<TN_Outbound_Plan>()
                    .Where(p => p.JHDH == model.jhdh).First();
                if (outboundPlan != null) {
                    return NewErpResult(1, preLog + $"计划单号'{model.jhdh}'已在[出库计划单]中存在!");
                    return NewErpResult(1, preLog + $"计划单号'{model.jhdh}'已在[出库计划单]中存在!");
                }
                outboundPlan = new TN_Outbound_Plan {
                    JHDH = model.jhdh, // 计划单号(唯一标识)
                    CKZT = model.ckzt, // 出库状态(需要返回)
                    JHDH = model.jhdh, // 计划单号 (唯一标识)
                    CKZT = model.ckzt, // 出库状态 (需要返回)
                    JHLB = model.jhlb, // 计划类别
                    CKDH = model.ckdh, // 参考单号
                    CPH = model.cph, // 车牌号
@@ -961,9 +933,9 @@
                    CPLB = model.cplb, // 产品类别
                    CPLBMX = model.cplbmx, // 产品类别明细
                    PP = model.pp, // 品牌
                    DJ = model.dj, // 等级(需要返回)
                    DJ = model.dj, // 等级 (需要返回)
                    GH = model.gh, // 罐号
                    PH = model.ph, // 批号(需要返回)
                    PH = model.ph, // 批号 (需要返回)
                    BZLX = model.bzlx, // 包装类型
                    PZDH = model.pzdh, // 派装单号
                    PZD_DW = model.pzd_dw, // 派装单单位
@@ -976,15 +948,15 @@
                    PZ_ZFRQ = model.pz_zfrq, // 派装作废日期
                    PZ_BZ = model.pz_bz, // 派装备注
                    CKDBH = model.ckdbh, // 出库单编号
                    SFJS = model.sfjs, // 实发件数(需要返回)
                    SFSL = model.sfsl, // 实发数量(需要返回)
                    SFCS = model.sfcs, // 实发车数(需要返回)
                    ZCSJ = model.zcsj, // 装车时间(需要返回)
                    JLDW = model.jldw, // 计量单位(需要返回)
                    FHRQ = model.fhrq, // 发货日期(需要返回)
                    CKDM = model.ckdm, // 仓库代码(需要返回)
                    FHR = model.fhr, // 发货人(需要返回)
                    CZYDM = model.czydm, // 操作员(需要返回)
                    SFJS = model.sfjs, // 实发件数 (需要返回)
                    SFSL = model.sfsl, // 实发数量 (需要返回)
                    SFCS = model.sfcs, // 实发车数 (需要返回)
                    ZCSJ = model.zcsj, // 装车时间 (需要返回)
                    JLDW = model.jldw, // 计量单位 (需要返回)
                    FHRQ = model.fhrq, // 发货日期 (需要返回)
                    CKDM = model.ckdm, // 仓库代码 (需要返回)
                    FHR = model.fhr, // 发货人 (需要返回)
                    CZYDM = model.czydm, // 操作员 (需要返回)
                    SHR_USERNAME = model.shr_username, // 审核人
                    SHRQ = model.shrq, // 审核日期
                    ZFBJ = model.zfbj, // 作废标记
@@ -993,14 +965,14 @@
                    SHDW = model.shdw, // 收货单位
                    YSDW = model.ysdw, // 运输单位
                    LXR = model.lxr, // 联系人
                    RY_ZXG = model.ry_zxg, // 装卸工(需要返回)
                    RY_CCSJ = model.ry_ccsj, // 叉车司机(需要返回)
                    RY_ZXG = model.ry_zxg, // 装卸工 (需要返回)
                    RY_CCSJ = model.ry_ccsj, // 叉车司机 (需要返回)
                    ERPHX_JHDH = model.erphx_jhdh, // erp交货单号
                    ERPHX_WLBM = model.erphx_wlbm, // erp物料编码
                    ERPHX_WLMC = model.erphx_wlmc, // erp物料名称
                    ERPHX_CJRQ = model.erphx_cjrq, // erp创建日期
                    HW = model.hw, // 货位(需要返回)
                    HWZT = model.hwzt // 货位状态(需要返回)
                    HW = model.hw, // 货位 (需要返回)
                    HWZT = model.hwzt // 货位状态 (需要返回)
                };
                //var cgDetailList = SelectCgByTotalQty(
@@ -1015,10 +987,9 @@
                //    return NewErpResult(3, info);
                //}
                var cgDetail = db.Queryable<TN_CG_Detail>()
                    .Where(d => d.S_ITEM_CODE == model.cpdm && d.N_ITEM_NUM >= model.pzjs).First();
                var cgDetail = db.Queryable<TN_CG_Detail>().Where(d => d.S_ITEM_CODE == model.cpdm && d.N_ITEM_NUM >= model.pzjs).First();
                if (cgDetail == null ) {
                    return NewErpResult(2, preLog + $"在[货品明细表]中没有找到合适的物料!要求:物料编码='{model.cpdm}',物料数量>={model.pzjs}");
                    return NewErpResult(2, preLog + $"在[货品明细表]中没有找到合适的物料!要求:物料编码='{model.cpdm}',物料数量>={model.pzjs}");
                }
                var order = new TN_Outbound_Order {
@@ -1063,25 +1034,25 @@
                using (var tran = db.Ado.UseTran()) {
                    if (db.Insertable(order).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewErpResult(500, preLog + $"生成[出库单]失败!数据:{LogObject(order)}");
                        return NewErpResult(500, preLog + $"生成[出库单]失败!数据:{LogObject(order)}");
                    }
                    if (db.Insertable(detailList).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewErpResult(500, preLog + $"生成[出库单明细]失败!数据:{LogObject(detailList)}");
                        return NewErpResult(500, preLog + $"生成[出库单明细]失败!数据:{LogObject(detailList)}");
                    }
                    if (db.Insertable(outboundPlan).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return NewErpResult(500, preLog + $"生成[出库单计划记录表]失败!数据:{LogObject(outboundPlan)}");
                        return NewErpResult(500, preLog + $"生成[出库单计划记录表]失败!数据:{LogObject(outboundPlan)}");
                    }
                    tran.CommitTran();
                    return NewErpResult(0, preLog + $"生成[出库单]成功!出库单:{LogObject(order)}\n出库单明细:{LogObject(detail)}");
                    return NewErpResult(0, preLog + $"生成[出库单]成功!出库单:{LogObject(order)}\n出库单明细:{LogObject(detail)}");
                }
            }
            catch (Exception ex) {
                return NewErpResult(1, preLog + $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewErpResult(1, preLog + $"发生了异常:{ex.Message}\n\n{ex.StackTrace}\n");
            }
        }
api/ApiModel.cs
@@ -17,16 +17,14 @@
            public List<object> result { get; set; } = new List<object>();
        }
        /// <summary>
        /// 构建 <see cref="SimpleResult"/> 返回值
        /// </summary>
        /// <param name="code"></param>
        /// <param name="message"></param>
        /// <param name="log"></param>
        /// <returns></returns>
        public static SimpleResult NewSimpleResult(int code, string message, bool log = true) {
            if (log) { LogHelper.Info(message); }
        public static SimpleResult NewSimpleResult(int code, string message, string name = "") {
            LogHelper.Info(message, name);
            return new SimpleResult { resultCode = code, resultMsg = message };
        }
        public static SimpleResult NewSimpleResult(Exception ex, string preLog = "", int errCode = -1) {
            LogHelper.InfoEx(ex, preLog);
            return new SimpleResult { resultCode = errCode, resultMsg = $"发生了[异常]:{ex.Message}" };
        }
        /// <summary>
@@ -60,7 +58,7 @@
            //public int station_id { get; set; }
            /// <summary>
            /// 请求上线/下线的的站台库位名称,例如work6、work8
            /// 请求上线/下线的的站台库位名称,例如work6,work8
            /// </summary>
            public string station_name { get; set; }
@@ -177,7 +175,7 @@
            [JsonProperty("n_num")]
            public int Num { get; set; }
            /// <summary>
            /// 起点货位信息(起点货位货区要求:MXCZQ 满箱操作区)
            /// 起点货位信息 (起点货位货区要求:MXCZQ 满箱操作区)
            /// </summary>
            [JsonProperty("s_start_loc")]
            public string StartLoc { get; set; }
@@ -193,17 +191,17 @@
            [JsonProperty("cntr_code")]
            public string CntrCode { get; set; }
            /// <summary>
            /// 容器类型(必须为 '空托盘’ 或 ‘空好运箱')
            /// 容器类型 (必须为 '空托盘’ 或 ‘空好运箱')
            /// </summary>
            [JsonProperty("cntr_type")]
            public string CntrType { get; set; }
            /// <summary>
            /// 终点库区编码(托盘是 KTCFQ 空托存放区;好运箱是 CXHJQ 空箱货架区)
            /// 终点库区编码 (托盘是 KTCFQ 空托存放区;好运箱是 CXHJQ 空箱货架区)
            /// </summary>
            [JsonProperty("end_area")]
            public string EndArea { get; set; }
            /// <summary>
            /// 起点货位(托盘是 KTJBQ 空托入库接驳区;好运箱是 KXJBQ 空箱入库接驳区)
            /// 起点货位 (托盘是 KTJBQ 空托入库接驳区;好运箱是 KXJBQ 空箱入库接驳区)
            /// </summary>
            [JsonProperty("start_loc")]
            public string StartLoc { get; set; }
@@ -275,7 +273,7 @@
            [JsonProperty("cntr_code")]
            public string CntrCode { get; set; }
            /// <summary>
            /// 不合格移库终点库区(必须是 CJYCQ 抽检异常区)
            /// 不合格移库终点库区 (必须是 CJYCQ 抽检异常区)
            /// </summary>
            [JsonProperty("end_area")]
            public string EndArea { get; set; }
@@ -368,17 +366,17 @@
            [JsonProperty("qualityGrade")]
            public string QualityGrade { get; set; }
            /// <summary>
            /// 料箱编号(待定)
            /// 料箱编号 (待定)
            /// </summary>
            [JsonProperty("cntrCode")]
            public string CntrCode { get; set; }
            /// <summary>
            /// 物料数量(待定)
            /// 物料数量 (待定)
            /// </summary>
            [JsonProperty("itemNum")]
            public int ItemNum { get; set; }
            ///// <summary>
            ///// 产线号(待定,好运箱有2条产线,对应2个下线货位,这里暂定为:3和4)
            ///// 产线号 (待定,好运箱有2条产线,对应2个下线货位,这里暂定为:3和4)
            ///// </summary>
            //[JsonProperty("prodLineId")]
            //public string ProdLineId { get; set; }
@@ -395,13 +393,13 @@
            public int Result { get; set; }
            /// <summary>
            /// 是否成功 True-成功,False:失败
            /// 是否成功 True-成功,False:失败
            /// </summary>
            [JsonProperty("success")]
            public bool Success { get; set; }
            /// <summary>
            /// 这里是string类型,对结果的描述
            /// 这里是string类型,对结果的描述
            /// </summary>
            [JsonProperty("data")]
            public string Data { get; set; }
@@ -418,7 +416,7 @@
            if (log) { LogHelper.Info(message); }
            return new MesResult {
                Result = code,
                Success = code == 0, // 仅当code=0时,success=true
                Success = code == 0, // 仅当code=0时,success=true
                Data = message,
            };
        }
@@ -443,7 +441,7 @@
        /// </summary>
        public class ErpSendOutboundPlanInfo {
            /// <summary>
            /// 计划单号(唯一标识)
            /// 计划单号 (唯一标识)
            /// </summary>
            public string jhdh { get; set; } = string.Empty;
@@ -705,7 +703,7 @@
        public class PickUpReturnErpInfo {
            /// <summary>
            /// 计划单号(唯一标识)
            /// 计划单号 (唯一标识)
            /// </summary>
            public string jhdh { get; set; }
@@ -967,7 +965,7 @@
        public class CreateTaskReturnErpInfo {
            /// <summary>
            /// 计划单号(唯一标识)
            /// 计划单号 (唯一标识)
            /// </summary>
            public string jhdh { get; set; }
api/DebugController.cs
@@ -21,7 +21,7 @@
namespace HH.WCS.Mobox3.DSZSH.api {
    /// <summary>
    /// 测试用:如果项目中要和设备对接,前期设备无法测试,用接口模拟
    /// 测试用:如果项目中要和设备对接,前期设备无法测试,用接口模拟
    /// </summary>
    [RoutePrefix("api")]
    public class DebugController : ApiController {
@@ -90,7 +90,7 @@
        }
        /// <summary>
        /// DEBUG:模拟输送线产线满托盘下线流程
        /// DEBUG:模拟输送线产线满托盘下线流程
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
@@ -101,7 +101,7 @@
        }
        /// <summary>
        /// DEBUG:模拟人工将料箱搬运到产线上线口(直接修改数据库)
        /// DEBUG:模拟人工将料箱搬运到产线上线口 (直接修改数据库)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
@@ -111,23 +111,23 @@
            var db = new SqlHelper<object>().GetInstance();
            try {
                // 查询起点货位:数量=0
                // 查询起点货位:数量=0
                var startLoc = db.Queryable<TN_Location>().LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE)
                    .Where((l,c) => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && l.S_CODE == model.StartLoc && l.N_CURRENT_NUM == 1 && c.S_CNTR_CODE == model.CntrCode).First();
                if (startLoc == null) {
                    return $"没有找到起点货位'{model.StartLoc}'!要求:锁状态='无',当前容器数量=1";
                    return $"没有找到起点货位'{model.StartLoc}'!要求:锁状态='无',当前容器数量=1";
                }
                // 查询终点货位
                // Order:按货位层数,从小到大排列
                // Order:按货位层数,从小到大排列
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && l.N_CURRENT_NUM == 0).First();
                if (endLoc == null) {
                    return $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0";
                    return $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0";
                }
                var locCntrRel = db.Queryable<TN_Loc_Container>().Where(c => c.S_CNTR_CODE == model.CntrCode).First() ;
                if (locCntrRel == null) {
                    return $"该容器不存在绑定的货位!";
                    return $"该容器不存在绑定的货位!";
                }
                locCntrRel.S_LOC_CODE = model.StartLoc;
@@ -140,7 +140,7 @@
                        db.Updateable(locCntrRel).ExecuteCommand() <= 0) {
                        
                        tran.RollbackTran();
                        return "数据库操作失败!";
                        return "数据库操作失败!";
                    }
                    tran.CommitTran() ;
@@ -154,7 +154,7 @@
        }
        /// <summary>
        /// DEBUG:模拟Erp下发出库计划单
        /// DEBUG:模拟Erp下发出库计划单
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
@@ -175,7 +175,7 @@
        }
        /// <summary>
        /// (内部方法请勿调用)模拟取货完成反馈Erp回报结果(默认为success)
        ///  (内部方法请勿调用) 模拟取货完成反馈Erp回报结果 (默认为success)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
@@ -196,7 +196,7 @@
        }
        /// <summary>
        /// (内部方法请勿调用)模拟任务创建完成反馈Erp回报结果(默认为success)
        ///  (内部方法请勿调用) 模拟任务创建完成反馈Erp回报结果 (默认为success)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
@@ -225,15 +225,15 @@
    public class TestErpSendOutboundPlanInfo {
        /// <summary>
        /// 出库计划单号(计划单号 jhdh)
        /// 出库计划单号 (计划单号 jhdh)
        /// </summary>
        public string PlanNo { get; set; } = string.Empty;
        /// <summary>
        /// 物料编码(产品代码 cpdm)
        /// 物料编码 (产品代码 cpdm)
        /// </summary>
        public string ItemCode { get; set; } = string.Empty;
        /// <summary>
        /// 物料数量(派装件数 pzjs)
        /// 物料数量 (派装件数 pzjs)
        /// </summary>
        public float ItemNum { get; set; } = 0;
    }
@@ -245,11 +245,11 @@
    public class AddInboundTaskInfo {
        /// <summary>
        /// 物料编码(暂时没用)
        /// 物料编码 (暂时没用)
        /// </summary>
        public string ItemCode { get; set; }
        /// <summary>
        /// 批次号(暂时没用)
        /// 批次号 (暂时没用)
        /// </summary>
        public string BatchNo { get; set; }
        /// <summary>
@@ -263,7 +263,7 @@
    }
    /// <summary>
    /// 模拟 AGV 传递信号,用于更改任务状态
    /// 模拟 AGV 传递信号,用于更改任务状态
    /// </summary>
    public class UpdateTaskState {
        /// <summary>
@@ -286,7 +286,7 @@
        /// </summary>
        public string ForkliftNo { get; set; }
        /// <summary>
        /// AGV 下一状态(任务回报号)
        /// AGV 下一状态 (任务回报号)
        /// </summary>
        public int NextState { set; get; } = 0;
    }
api/MoboxController.cs
@@ -41,8 +41,8 @@
            else if (model.CntrType == "空好运箱") {
                return ApiHelper.EmptyInboundGoodpack(model);
            }
            else { // PDA前端下拉选单限制,故理论上不会进入这个流程
                return NewSimpleResult(-1, $"容器类型'{model.CntrType}'不合法!要求:类型=['空托盘','空好运箱']");
            else { // PDA前端下拉选单限制,故理论上不会进入这个流程
                return NewSimpleResult(-1, $"容器类型'{model.CntrType}'不合法!要求:类型=['空托盘','空好运箱']");
            }
        }
        
config/config.comment.json
@@ -3,15 +3,15 @@
    "NdcApiUrl": "http://127.0.0.1:5201/api/order/", // NDC AGV接口地址
    "ErpApiUrl": "http://127.0.0.1:8901/api/", // ERP接口地址
    "ErpRoute": {
        "CreateTaskReturn": "CreateTaskReturn", // 根据ERP下发出库计划,创建任务后反馈ERP的接口
        "PickUpReturn": "PickUpReturn"          // 根据ERP下发出库计划,AGV取货后反馈ERP的接口
        "CreateTaskReturn": "CreateTaskReturn", // 根据ERP下发出库计划,创建任务后反馈ERP的接口
        "PickUpReturn": "PickUpReturn"          // 根据ERP下发出库计划,AGV取货后反馈ERP的接口
    },
    "SqlServer": "Data Source=192.168.1.87;Initial Catalog=AMS_OIMobox;User ID=sa;Password=123456;", // 数据库配置
    "TaskInfos": [ // 任务信息(名称、起点货区、终点货区)
    "TaskInfos": [ // 任务信息 (名称,起点货区,终点货区)
        {
            "TaskName": "满托下线入库", // 任务名称(可以更改,但各任务相对位置不能更改)
            "StartAreas": [ "BZQ" ], // 起点货区编号:包装区
            "EndAreas": [ "MTHJQ" ] // 终点货区编号:满托货架区
            "TaskName": "满托下线入库", // 任务名称 (可以更改,但各任务相对位置不能更改)
            "StartAreas": [ "BZQ" ], // 起点货区编号:包装区
            "EndAreas": [ "MTHJQ" ] // 终点货区编号:满托货架区
        },
        {
            "TaskName": "满箱下线入库",
@@ -20,8 +20,8 @@
        },
        {
            "TaskName": "成品胶出库",
            "StartAreas": [ "MTHJQ", "MXHJQ", "HCBHQ", "QCBHQ" ], // 满托货架区、满箱货架区、火车备货区、汽车备货区
            "EndAreas": [ "HCCKQ", "QCCKQ" ] // 火车出库区、汽车出库区
            "StartAreas": [ "MTHJQ", "MXHJQ", "HCBHQ", "QCBHQ" ], // 满托货架区,满箱货架区,火车备货区,汽车备货区
            "EndAreas": [ "HCCKQ", "QCCKQ" ] // 火车出库区,汽车出库区
        },
        {
            "TaskName": "空托上线出库",
@@ -40,19 +40,19 @@
        },
        {
            "TaskName": "空箱入库",
            "StartAreas": [ "KXJBQ1", "KXJBQ2" ], // 空箱入库接驳区1、2
            "StartAreas": [ "KXJBQ1", "KXJBQ2" ], // 空箱入库接驳区1,2
            "EndAreas": [ "KXHJQ" ] // 空箱货架区
        },
        {
            "TaskName": "抽检出库",
            "StartAreas": [ "MTHJQ", "MXHJQ" ], // 满托货架区、满箱货架区
            "StartAreas": [ "MTHJQ", "MXHJQ" ], // 满托货架区,满箱货架区
            "EndAreas": [ "CJQ" ] // 抽检区
        },
        {
            "TaskName": "抽检合格回库",
            "StartAreas": [ "CJQ" ], // 抽检区
            "EndAreas_Pallet": [ "MTHJQ" ], // 终点货区(托盘):满托货架区
            "EndAreas_Goodpack": [ "MXHJQ" ] // 终点货区(好运箱):满箱货架区
            "EndAreas_Pallet": [ "MTHJQ" ], // 终点货区 (托盘) :满托货架区
            "EndAreas_Goodpack": [ "MXHJQ" ] // 终点货区 (好运箱) :满箱货架区
        },
        {
            "TaskName": "抽检不合格移库",
@@ -69,13 +69,13 @@
            "TaskName": "移库"
        }
    ],
    "ProductionLines": [ // 产线信息(待定,根据后面需求再更改)
    "ProductionLines": [ // 产线信息 (待定,根据后面需求再更改)
        {
            "Id": "1", // 产线编号
            "Name": "托盘产线1", // 产线名称
            "PlcIp": "127.0.0.1", // 产线IP
            "PlcPort": 502, // 产线端口
            "SlaveId": 1, // 产线modbus slave id(根据实际情况)
            "SlaveId": 1, // 产线modbus slave id (根据实际情况)
            "OnLoc": [ "BZQ-1-1" ], // 上线货位
            "OffLoc": [ "BZQ-1-2" ] // 下线货位
        },
core/Monitor.cs
@@ -33,10 +33,10 @@
                    //TcpClientHelper.Link("127.0.0.1", 8550); // 用于测试
                    TcpClientHelper.Link(prod.PlcIp, prod.PlcPort);
                    // TCPClient传递信息的时候,不要用断点阻塞程序
                    // TCPClient传递信息的时候,不要用断点阻塞程序
                    // {"item_code":"CG1001","batch_no":"BN1001","cntr_code":"CN2505111"}
                    if (!TcpClientHelper.TryReadProductionLine(out byte[] read)) {
                        info = $"测试{prod.Id}号产线{prod.PlcIp}:{prod.PlcPort}:读取产线信息失败";
                        info = $"测试{prod.Id}号产线{prod.PlcIp}:{prod.PlcPort}:读取产线信息失败";
                        LogHelper.Info(info);
                        continue;
                    }
@@ -72,20 +72,14 @@
        public static void CheckOutboundOrder() {
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            var taskInfo = Settings.GetTaskInfo(ETask.C成品胶出库);
            var taskName = taskInfo.TaskName;
            var taskName = "出库";
            const string preLog = "轮询:出库:";
            try {
                var orderList = db.Queryable<TN_Outbound_Order>()
                    .Where(c => c.N_B_STATE == 1)
                    .OrderBy(c => c.T_CREATE, SqlSugar.OrderByType.Asc)
                    .ToList();
                var orderList = db.Queryable<TN_Outbound_Order>().Where(c => c.N_B_STATE == 1).OrderBy(c => c.T_CREATE).ToList();
                if (orderList.Count == 0) {
                    info = $"轮询:{taskName}:暂无待执行的{taskName}单";
                    LogHelper.Info(info);
                    LogHelper.Debug(preLog + $"暂无待执行的{taskName}单");
                    return;
                }
@@ -93,12 +87,9 @@
                var detailList = new List<TN_Outbound_Detail>();
                foreach (var order in orderList) {
                    var doingCount = db.Queryable<TN_Outbound_Detail>()
                        .Count(d => d.S_OO_NO == order.S_NO && d.N_B_STATE >= 2); // 执行中
                    var allCount = db.Queryable<TN_Outbound_Detail>()
                        .Count(d => d.S_OO_NO == order.S_NO);
                    info = $"轮询:{taskName}:统计{taskName}单'{order.S_NO}'任务已下发:{doingCount}/{allCount}";
                    LogHelper.Info(info);
                    var doingCount = db.Queryable<TN_Outbound_Detail>().Count(d => d.S_OO_NO == order.S_NO && d.N_B_STATE >= 2);
                    var allCount = db.Queryable<TN_Outbound_Detail>().Count(d => d.S_OO_NO == order.S_NO);
                    LogHelper.Info(preLog + $"统计{taskName}单'{order.S_NO}'任务已下发:{doingCount}/{allCount}");
                    if (doingCount == allCount) {
                        order.N_B_STATE = 2; // 所有任务都已执行
@@ -111,8 +102,7 @@
                        .First();
                    if (lastDetail != null) {
                        info = $"轮询:{taskName}:{taskName}单'{order.S_NO}'上一个任务仍在进行中:" + JsonConvert.SerializeObject(lastDetail);
                        LogHelper.Info(info);
                        LogHelper.Info(preLog + $"{taskName}单'{order.S_NO}'上一个任务仍在进行中:" + JsonConvert.SerializeObject(lastDetail));
                        continue;
                    }
@@ -121,8 +111,7 @@
                        .First();
                    if (outboundDetail == null) {
                        info = $"轮询:{taskName}:仍有任务未执行完成,但当前没有已下发的任务";
                        LogHelper.Info(info);
                        LogHelper.Info(preLog + $"仍有任务未执行完成,但当前没有已下发的任务!");
                        continue;
                    }
@@ -135,16 +124,10 @@
                }
                foreach (var detail in detailList) {
                    var startLoc = db.Queryable<TN_Location>()
                        .LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE)
                        .Where((l, c) => c.S_CNTR_CODE == detail.S_CNTR_CODE) // 根据容器号搜索起点货位
                        .Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y") // 筛选:未上锁
                        .Where(l => l.N_CURRENT_NUM == 1)
                        .First();
                    var startLoc = db.Queryable<TN_Location>().LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE).Where((l, c) => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && l.N_CURRENT_NUM == 1 && c.S_CNTR_CODE == detail.S_CNTR_CODE).First();
                    if (startLoc == null) {
                        info = $"轮询:{taskName}:没有找到合适的起点货位!";
                        LogHelper.Info(info);
                        LogHelper.Info(preLog + $"没有找到合适的起点货位!");
                        continue;
                    }
@@ -154,8 +137,7 @@
                        .Where(a => a.N_CURRENT_NUM == 0).First();
                    if (endLoc == null) {
                        info = $"轮询:{taskName}:没有找到合适的终点货位!单号'{detail.S_OO_NO}'要求终点库区为'{detail.S_END_AREA}'";
                        LogHelper.Info(info);
                        LogHelper.Info(preLog + $"没有找到合适的终点货位!单号'{detail.S_OO_NO}'要求终点库区为'{detail.S_END_AREA}'");
                        continue;
                    }
                    
@@ -176,50 +158,30 @@
                    using (var tran = db.Ado.UseTran()) {
                        if (db.Updateable(detail).UpdateColumns(it => it.N_B_STATE).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"轮询:{taskName}:修改明细表状态为完成--失败!";
                            LogHelper.Info(info);
                            LogHelper.Info($"轮询:{taskName}:修改明细表状态为完成--失败!");
                            continue;
                        }
                        if (db.Updateable(startLoc).UpdateColumns(it => new {
                            it.N_LOCK_STATE,
                            it.S_LOCK_STATE,
                            it.S_LOCK_OP,
                            it.T_MODIFY,
                            it.N_CURRENT_NUM, // 起点货位绑定后,将货位状态更新
                        }).ExecuteCommand() <= 0) {
                        // 更新[起点/终点]锁状态,创建任务
                        if (db.Updateable(startLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, it.N_CURRENT_NUM, }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成任务'{taskName}'失败:更新起点货位{startLoc.S_CODE}锁状态失败";
                            LogHelper.Info(info);
                            continue;
                            LogHelper.Info(preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                        }
                        if (db.Updateable(endLoc).UpdateColumns(it => new {
                            it.N_LOCK_STATE,
                            it.S_LOCK_STATE,
                            it.S_LOCK_OP,
                            it.T_MODIFY,
                        }).ExecuteCommand() <= 0) {
                        if (db.Updateable(endLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成任务'{taskName}'失败:更新终点货位{endLoc.S_CODE}锁状态失败";
                            LogHelper.Info(info);
                            continue;
                            LogHelper.Info(preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                        }
                        if (db.Insertable(task).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成任务'{taskName}'失败,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                            LogHelper.Info(info);
                            continue;
                            LogHelper.Info(preLog + $"生成任务'{task.S_TYPE}'失败!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                        }
                        // 提交数据库更改
                        tran.CommitTran();
                        info = $"生成任务'{taskName}'成功,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                        LogHelper.Info(info);
                        //continue;
                        LogHelper.Info(preLog + $"生成任务'{task.S_TYPE}'成功!任务号={task.S_CODE},容器号={task.S_CNTR_CODE},起点={task.S_START_LOC},终点={task.S_END_LOC}");
                    }
                    // 如果当前出库单明细是ERP下发的,任务创建完成反馈货位信息
                    // 如果当前出库单明细是ERP下发的,任务创建完成反馈货位信息
                    if (detail.S_BS_TYPE == "ERP") {
                        //var createTaskReturnErpTask = Task.Run(() => {
                        //    CreateTaskReturnErp(task);
@@ -231,8 +193,7 @@
            }
            catch (Exception ex) {
                info = $"轮询:{taskName}:发生了异常:{ex.Message}";
                LogHelper.InfoEx(ex);
                LogHelper.InfoEx(ex, preLog);
            }
        }
@@ -242,25 +203,20 @@
            var taskInfo = Settings.GetTaskInfo(ETask.C抽检出库);
            var taskName = taskInfo.TaskName;
            const string preLog = "轮询:抽检:";
            try {
                var orderList = db.Queryable<TN_Spot_Check>()
                    .Where(c => c.N_B_STATE == 1)
                    .OrderBy(c => c.T_CREATE, SqlSugar.OrderByType.Asc)
                    .ToList();
                var orderList = db.Queryable<TN_Spot_Check>().Where(c => c.N_B_STATE == 1).OrderBy(c => c.T_CREATE).ToList();
                if (orderList.Count == 0) {
                    LogHelper.Info($"轮询:{taskName}:暂无待执行的{taskName}单");
                    LogHelper.Debug($"轮询:{taskName}:暂无待执行的{taskName}单");
                    return;
                }
                var detailList = new List<TN_SpotCheck_Detail>();
                foreach (var order in orderList) {
                    var doingCount = db.Queryable<TN_SpotCheck_Detail>()
                        .Count(d => d.S_OO_NO == order.S_NO && d.N_B_STATE >= 2); // 执行中
                    var allCount = db.Queryable<TN_SpotCheck_Detail>()
                        .Count(d => d.S_OO_NO == order.S_NO);
                    LogHelper.Info($"轮询:{taskName}:统计{taskName}单'{order.S_NO}'任务已下发:{doingCount}/{allCount}");
                    var doingCount = db.Queryable<TN_SpotCheck_Detail>().Count(d => d.S_OO_NO == order.S_NO && d.N_B_STATE >= 2);
                    var allCount = db.Queryable<TN_SpotCheck_Detail>().Count(d => d.S_OO_NO == order.S_NO);
                    LogHelper.Info(preLog + $"统计{taskName}单'{order.S_NO}'任务已下发:{doingCount}/{allCount}");
                    if (doingCount == allCount) {
                        order.N_B_STATE = 2; // 所有任务都已执行
@@ -273,7 +229,7 @@
                        .ToList();
                    if (checkDetailList.Count == 0) {
                        LogHelper.Info($"轮询:{taskName}:仍有任务未执行完成,但当前没有已下发的任务");
                        LogHelper.Info($"轮询:{taskName}:仍有任务未执行完成,但当前没有已下发的任务");
                        continue;
                    }
@@ -283,23 +239,21 @@
                }
                foreach (var detail in detailList) {
                    var startLoc = db.Queryable<TN_Location>()
                        .LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE)
                        .Where((l, c) => c.S_CNTR_CODE == detail.S_CNTR_CODE)
                        .First();
                    var startLoc = db.Queryable<TN_Location>() .LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE)
                        .Where((l, c) => c.S_CNTR_CODE == detail.S_CNTR_CODE).First();
                    if (startLoc == null) {
                        LogHelper.Info($"轮询:{taskName}:没有找到合适的起点货位!");
                        LogHelper.Info($"轮询:{taskName}:没有找到合适的起点货位!");
                        continue;
                    }
                    var endLoc = db.Queryable<TN_Location>()
                        .Where(l => l.S_AREA_CODE == detail.S_END_AREA)
                        .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                        .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                        .Where(a => a.N_CURRENT_NUM == 0).First();
                    if (endLoc == null) {
                        LogHelper.Info($"轮询:{taskName}:没有找到合适的终点货位!");
                        LogHelper.Info($"轮询:{taskName}:没有找到合适的终点货位!");
                        continue;
                    }
@@ -309,57 +263,44 @@
                    var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                    task.S_OP_CODE = detail.S_OO_NO;
                    WCSHelper.LockStartLoc(ref startLoc); // 起点出库锁
                    WCSHelper.LockEndLoc(ref endLoc); // 终点入库锁
                    WCSHelper.LockStartLoc(ref startLoc);
                    WCSHelper.LockEndLoc(ref endLoc);
                    using (var tran = db.Ado.UseTran()) {
                        if (db.Updateable(detail).UpdateColumns(it => it.N_B_STATE).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            LogHelper.Info($"轮询:{taskName}:修改{taskName}单明细表状态为完成--失败!");
                            LogHelper.Info($"轮询:{taskName}:修改{taskName}单明细表状态为完成--失败!");
                            continue;
                        }
                        if (db.Updateable(startLoc).UpdateColumns(it => new {
                            it.N_LOCK_STATE,
                            it.S_LOCK_STATE,
                            it.S_LOCK_OP,
                            it.T_MODIFY,
                            it.N_CURRENT_NUM, // 起点货位绑定后,将货位状态更新
                        }).ExecuteCommand() <= 0) {
                        if (db.Updateable(startLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, it.N_CURRENT_NUM, }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成任务'{taskName}'失败:更新起点货位{startLoc.S_CODE}锁状态失败";
                            LogHelper.Info(info);
                            LogHelper.Info(preLog + $"更新[起点货位锁状态]失败!起点='{startLoc.S_CODE}',锁状态=>'出库锁'");
                            continue;
                        }
                        if (db.Updateable(endLoc).UpdateColumns(it => new {
                            it.N_LOCK_STATE,
                            it.S_LOCK_STATE,
                            it.S_LOCK_OP,
                            it.T_MODIFY,
                        }).ExecuteCommand() <= 0) {
                        if (db.Updateable(endLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成任务'{taskName}'失败:更新终点货位{endLoc.S_CODE}锁状态失败";
                            LogHelper.Info(info);
                            LogHelper.Info(preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                            continue;
                        }
                        if (db.Insertable(task).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成任务'{taskName}'失败,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                            info = $"生成任务'{taskName}'失败,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                            LogHelper.Info(info);
                            continue;
                        }
                        tran.CommitTran();
                        info = $"生成任务'{taskName}'成功,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                        info = $"生成任务'{taskName}'成功,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                        LogHelper.Info(info);
                        continue;
                    }
                }
            }
            catch (Exception ex) {
                LogHelper.InfoEx(ex);
                LogHelper.InfoEx(ex, preLog);
            }
        }
@@ -369,6 +310,7 @@
            var taskInfo = Settings.GetTaskInfo(ETask.Y移库);
            var taskName = taskInfo.TaskName;
            const string preLog = "轮询:移库:";
            try {
                var orderList = db.Queryable<TN_Relocation_List>()
@@ -377,7 +319,7 @@
                    .ToList();
                if (orderList.Count == 0) {
                    LogHelper.Info($"轮询:{taskName}:暂无待执行的{taskName}单");
                    LogHelper.Debug($"轮询:{taskName}:暂无待执行的{taskName}单");
                    return;
                }
@@ -387,7 +329,7 @@
                        .Count(d => d.S_OO_NO == order.S_NO && d.N_B_STATE >= 2); // 执行中
                    var allCount = db.Queryable<TN_RelocationList_Detail>()
                        .Count(d => d.S_OO_NO == order.S_NO);
                    LogHelper.Info($"轮询:{taskName}:统计{taskName}单={order.S_NO}任务已下发:{doingCount}/{allCount}");
                    LogHelper.Info($"轮询:{taskName}:统计{taskName}单={order.S_NO}任务已下发:{doingCount}/{allCount}");
                    if (doingCount == allCount) {
                        order.N_B_STATE = 2; // 所有任务都已执行
@@ -400,7 +342,7 @@
                        .ToList();
                    if (checkDetailList.Count == 0) {
                        LogHelper.Info($"轮询:{taskName}:仍有任务未执行完成,但当前没有已下发的任务");
                        LogHelper.Info($"轮询:{taskName}:仍有任务未执行完成,但当前没有已下发的任务");
                        continue;
                    }
@@ -413,23 +355,23 @@
                    var startLoc = db.Queryable<TN_Location>()
                        .LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE)
                        .Where((l, c) => c.S_CNTR_CODE == detail.S_CNTR_CODE)
                        .Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y") // 筛选:未上锁
                        .Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y") // 筛选:未上锁
                        .Where(l => l.N_CURRENT_NUM == 1)
                        .First();
                    if (startLoc == null) {
                        info = $"轮询:{taskName}:没有找到容器号={detail.S_CNTR_CODE}的起点货位!需要满足:未上锁、当前容器数量=1";
                        info = $"轮询:{taskName}:没有找到容器号={detail.S_CNTR_CODE}的起点货位!需要满足:未上锁,当前容器数量=1";
                        LogHelper.Info(info);
                        continue;
                    }
                    var endLoc = db.Queryable<TN_Location>()
                        .Where(l => l.S_AREA_CODE == detail.S_END_AREA)
                        .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                        .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                        .Where(a => a.N_CURRENT_NUM == 0).First();
                    if (endLoc == null) {
                        info = $"轮询:{taskName}:没有找到终点货位={detail.S_END_AREA}的终点货位!需要满足:未上锁、当前容器数量=0";
                        info = $"轮询:{taskName}:没有找到终点货位={detail.S_END_AREA}的终点货位!需要满足:未上锁,当前容器数量=0";
                        LogHelper.Info(info);
                        continue;
                    }
@@ -446,7 +388,7 @@
                    using (var tran = db.Ado.UseTran()) {
                        if (db.Updateable(detail).UpdateColumns(it => it.N_B_STATE).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            LogHelper.Info($"轮询:{taskName}:修改{taskName}单明细表状态为完成--失败!");
                            LogHelper.Info($"轮询:{taskName}:修改{taskName}单明细表状态为完成--失败!");
                            continue;
                        }
@@ -455,10 +397,10 @@
                            it.S_LOCK_STATE,
                            it.S_LOCK_OP,
                            it.T_MODIFY,
                            it.N_CURRENT_NUM, // 起点货位绑定后,将货位状态更新
                            it.N_CURRENT_NUM, // 起点货位绑定后,将货位状态更新
                        }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成任务'{taskName}'失败:更新起点货位{startLoc.S_CODE}锁状态失败";
                            info = $"生成任务'{taskName}'失败:更新起点货位{startLoc.S_CODE}锁状态失败";
                            LogHelper.Info(info);
                            continue;
                        }
@@ -470,27 +412,27 @@
                            it.T_MODIFY,
                        }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成任务'{taskName}'失败:更新终点货位{endLoc.S_CODE}锁状态失败";
                            info = $"生成任务'{taskName}'失败:更新终点货位{endLoc.S_CODE}锁状态失败";
                            LogHelper.Info(info);
                            continue;
                        }
                        if (db.Insertable(task).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成任务'{taskName}'失败,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                            info = $"生成任务'{taskName}'失败,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                            LogHelper.Info(info);
                            continue;
                        }   
                        tran.CommitTran();
                        info = $"生成任务'{taskName}'成功,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                        info = $"生成任务'{taskName}'成功,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                        LogHelper.Info(info);
                        continue;
                    }
                }
            }
            catch (Exception ex) {
                LogHelper.InfoEx(ex);
                LogHelper.InfoEx(ex, preLog);
            }
        }
@@ -505,7 +447,7 @@
                    .First();
                if (plan == null) {
                    info = $"计划单号{task.S_BS_NO}不存在!";
                    info = $"计划单号{task.S_BS_NO}不存在!";
                    LogHelper.Info(info);
                }
@@ -514,13 +456,13 @@
                    .First();
                if (cgDetail == null) {
                    info = $"物料编码不存在!";
                    info = $"物料编码不存在!";
                    LogHelper.Info(info);
                }
                var model = new OtherModel.CreateTaskReturnErpInfo {
                    jhdh = plan.JHDH , // 计划单号(唯一标识)
                    ckzt = plan.CKZT , // 出库状态(需要返回)
                    jhdh = plan.JHDH , // 计划单号 (唯一标识)
                    ckzt = plan.CKZT , // 出库状态 (需要返回)
                    jhlb = plan.JHLB , // 计划类别
                    ckdh = plan.CKDH , // 参考单号
                    cph = plan.CPH , // 车牌号
@@ -531,9 +473,9 @@
                    cplb = plan.CPLB , // 产品类别
                    cplbmx = plan.CPLBMX , // 产品类别明细
                    pp = plan.PP , // 品牌
                    dj = plan.DJ , // 等级(需要返回)
                    dj = plan.DJ , // 等级 (需要返回)
                    gh = plan.GH , // 罐号
                    ph = plan.PH , // 批号(需要返回)
                    ph = plan.PH , // 批号 (需要返回)
                    bzlx = plan.BZLX , // 包装类型
                    pzdh = plan.PZDH , // 派装单号
                    pzd_dw = plan.PZD_DW , // 派装单单位
@@ -546,15 +488,15 @@
                    pz_zfrq = plan.PZ_ZFRQ , // 派装作废日期
                    pz_bz = plan.PZ_BZ , // 派装备注
                    ckdbh = plan.CKDBH , // 出库单编号
                    //sfjs = plan.SFJS , // 实发件数(需要返回)
                    //sfsl = plan.SFSL , // 实发数量(需要返回)
                    //sfcs = plan.SFCS , // 实发车数(需要返回)
                    //zcsj = plan.ZCSJ , // 装车时间(需要返回)
                    //jldw = plan.JLDW , // 计量单位(需要返回)
                    //fhrq = plan.FHRQ , // 发货日期(需要返回)
                    //ckdm = plan.CKDM , // 仓库代码(需要返回)
                    //fhr = plan.FHR , // 发货人(需要返回)
                    //czydm = plan.CZYDM , // 操作员(需要返回)
                    //sfjs = plan.SFJS , // 实发件数 (需要返回)
                    //sfsl = plan.SFSL , // 实发数量 (需要返回)
                    //sfcs = plan.SFCS , // 实发车数 (需要返回)
                    //zcsj = plan.ZCSJ , // 装车时间 (需要返回)
                    //jldw = plan.JLDW , // 计量单位 (需要返回)
                    //fhrq = plan.FHRQ , // 发货日期 (需要返回)
                    //ckdm = plan.CKDM , // 仓库代码 (需要返回)
                    //fhr = plan.FHR , // 发货人 (需要返回)
                    //czydm = plan.CZYDM , // 操作员 (需要返回)
                    shr_username = plan.SHR_USERNAME , // 审核人
                    shrq = plan.SHRQ , // 审核日期
                    zfbj = plan.ZFBJ , // 作废标记
@@ -563,14 +505,14 @@
                    shdw = plan.SHDW , // 收货单位
                    ysdw = plan.YSDW , // 运输单位
                    lxr = plan.LXR , // 联系人
                    //ry_zxg = plan.RY_ZXG , // 装卸工(需要返回)
                    //ry_ccsj = plan.RY_CCSJ , // 叉车司机(需要返回)
                    //ry_zxg = plan.RY_ZXG , // 装卸工 (需要返回)
                    //ry_ccsj = plan.RY_CCSJ , // 叉车司机 (需要返回)
                    erphx_jhdh = plan.ERPHX_JHDH , // erp交货单号
                    erphx_wlbm = plan.ERPHX_WLBM , // erp物料编码
                    erphx_wlmc = plan.ERPHX_WLMC , // erp物料名称
                    erphx_cjrq = plan.ERPHX_CJRQ , // erp创建日期
                    hw = plan.HW , // 货位(需要返回)
                    hwzt  = plan.HWZT // 货位状态(需要返回)
                    hw = plan.HW , // 货位 (需要返回)
                    hwzt  = plan.HWZT // 货位状态 (需要返回)
                };
                model.hw = task.S_START_LOC;
                model.hwzt = "待出库";
@@ -579,7 +521,7 @@
                var jsonInfo = JsonConvert.SerializeObject(model);
                var result = httpH.WebPost(Settings.ErpApiUrl + Settings.ErpRoute.CreateTaskReturn, jsonInfo);
                LogHelper.InfoApi($"创建任务完成反馈ERP接口,结果={result},调用参数:", model);
                LogHelper.InfoApi($"创建任务完成反馈ERP接口,结果={result},调用参数:", model);
                plan.HW = model.hw;
                plan.HWZT = model.hwzt;
core/WCSCore.cs
@@ -22,26 +22,26 @@
namespace HH.WCS.Mobox3.DSZSH.core {
    public class WCSCore {
        public static ReturnResult OperateAgvTaskStatus(AgvTaskState model) {
            const string preLog = "AGV:任务状态回报:";
            const string preLog = "AGV:任务状态回报:";
            try {
                if (model.state > 0) {
                    // AGV 执行任务的逻辑处理
                    if (!AgvTaskProcessOk(model)) {
                        // 执行不OK,说明没有找到任务
                        return NewReturnResult(1, preLog + $"根据任务号'{model.task_no}'未找到对应的任务!");
                        // 执行不OK,说明没有找到任务
                        return NewReturnResult(1, preLog + $"根据任务号'{model.task_no}'未找到对应的任务!");
                    }
                }
                return NewReturnResult(0, "success"); // 不返回详细成功日志,避免NDC以msg=success作为成功判定(参考国自)
                return NewReturnResult(0, "success"); // 不返回详细成功日志,避免NDC以msg=success作为成功判定 (参考国自)
            }
            catch (Exception ex) {
                return NewReturnResult(-1, $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewReturnResult(-1, $"发生了异常:{ex.Message}\n\n{ex.StackTrace}\n");
            }
        }
        /// <summary>
        /// 执行 AGV 任务,查询不到任务返回 <see langword="false"/>
        /// 执行 AGV 任务,查询不到任务返回 <see langword="false"/>
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
@@ -104,9 +104,9 @@
                    break;
            }
            // 将AGV执行状态,加入TN_Task_Action表中
            // 将AGV执行状态,加入TN_Task_Action表中
            WCSHelper.AddActionRecord(model.task_no, model.state, model.forklift_no, model.ext_data);
            //调用第三方接口(如果有)TaskProcess.ReportStatus,添加任务动作关系表
            //调用第三方接口 (如果有) TaskProcess.ReportStatus,添加任务动作关系表
            return true;
        }
@@ -116,19 +116,19 @@
            var info = "";
            try {
                var cgDetail = new TN_CG_Detail(); // 如果没有信息,默认就是空值,可以直接填入,不需要判断
                var cgDetail = new TN_CG_Detail(); // 如果没有信息,默认就是空值,可以直接填入,不需要判断
                
                //var emptyTask = false; // 空托/空箱任务
                if (task.S_TYPE != ETask.K空托上线出库.Name() && task.S_TYPE != ETask.K空托入库.Name() &&
                    task.S_TYPE != ETask.K空箱上线出库.Name() && task.S_TYPE != ETask.K空箱入库.Name()) {
                    // 非空托/空箱任务(空托/空箱任务无法在CGDetail查到物料信息)
                    // 非空托/空箱任务 (空托/空箱任务无法在CGDetail查到物料信息)
                    //emptyTask= true;
                    cgDetail = db.Queryable<TN_CG_Detail>()
                        .Where(d => d.S_CNTR_CODE == task.S_CNTR_CODE)
                        .First();
                    if (cgDetail == null) {
                        info = $"任务{task.S_CODE}完成,记录出入库存在问题:无法在容器货品明细表中找到托盘{task.S_CNTR_CODE}对应的物料";
                        info = $"任务{task.S_CODE}完成,记录出入库存在问题:无法在容器货品明细表中找到托盘{task.S_CNTR_CODE}对应的物料";
                        LogHelper.Info(info);
                        //return;
                        cgDetail = new TN_CG_Detail() ;
@@ -136,7 +136,7 @@
                    }
                }
                var isInbound = false; // 入库类型的任务(包括移库类任务)
                var isInbound = false; // 入库类型的任务 (包括移库类任务)
                var inboundTasks = new List<string> {
                    ETask.M满托下线入库.Name(), ETask.M满箱下线入库.Name(), ETask.K空托入库.Name(), ETask.K空箱入库.Name(),
                    ETask.C抽检合格回库.Name(), ETask.C抽检不合格移库.Name(), ETask.Y移库.Name()
@@ -159,7 +159,7 @@
                    S_ITEM_CODE = cgDetail.S_ITEM_CODE,
                    S_BATCH_NO = cgDetail.S_BATCH_NO,
                    S_ITEM_NAME = cgDetail.S_ITEM_NAME,
                    S_LOC_CODE = isInbound ? task.S_END_LOC : task.S_START_LOC, // 入库记录终点货位,出库记录起点货位
                    S_LOC_CODE = isInbound ? task.S_END_LOC : task.S_START_LOC, // 入库记录终点货位,出库记录起点货位
                    S_CNTR_CODE = task.S_CNTR_CODE,
                    S_ITEM_SPEC = cgDetail.S_ITEM_SPEC,
                    S_NET_WEIGHT = cgDetail.S_NET_WEIGHT,
@@ -168,14 +168,14 @@
                    S_TASK_NO = task.S_CODE,
                    T_RECORD_TIME = DateTime.Now,
                    S_TYPE = task.S_TYPE,
                    S_BS_CODE = task.S_BS_NO, // ERP单号,默认为空
                    S_BS_CODE = task.S_BS_NO, // ERP单号,默认为空
                    N_QTY = cgDetail.N_ITEM_NUM,
                    S_NO = task.S_OP_CODE, // 出库/抽检/移库单
                };
                // 数据库操作
                if (db.Insertable(record).ExecuteCommand() <= 0) {
                    info = "插入出入库记录表失败:" + JsonConvert.SerializeObject(record);
                    info = "插入出入库记录表失败:" + JsonConvert.SerializeObject(record);
                    LogHelper.Info(info);
                    return;
                }
@@ -195,7 +195,7 @@
        /// <returns></returns>
        public static ReturnResult SafetyInteraction(SafetyInteractionInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            const string preLog = "AGV:产线安全交互:";
            const string preLog = "AGV:产线安全交互:";
            try {
                ModbusHelper.Relink();
@@ -233,13 +233,13 @@
                //}
                if (prodLineDevice.SystemState != 1) {
                    return NewReturnResult(3, preLog + $"当前产线无法与AGV联动:状态{prodLineDevice.SystemState}");
                    return NewReturnResult(3, preLog + $"当前产线无法与AGV联动:状态{prodLineDevice.SystemState}");
                }
                // 请求取货
                if (model.apply_code == "5") {
                    if (prodLineDevice.FullOffline != 1) {
                        return NewReturnResult(4, preLog + $"当前输送线满料下线信号不为1,无法取货");
                        return NewReturnResult(4, preLog + $"当前输送线满料下线信号不为1,无法取货");
                    }
                    
                    if (!prodLineDevice.SetAgvPicking(1)) {
@@ -251,7 +251,7 @@
                // 请求卸货
                else if (model.apply_code == "1") {
                    if (prodLineDevice.AllowAgvPlacePallet != 1) {
                        return NewReturnResult(6, preLog + $"当前输送线允许放托盘信号不为1,无法放货");
                        return NewReturnResult(6, preLog + $"当前输送线允许放托盘信号不为1,无法放货");
                    }
                    if (!prodLineDevice.SetAgvPlacingPallet(1)) {
@@ -266,7 +266,7 @@
            }
            catch (Exception ex) {
                return NewReturnResult(1, preLog + $"发生了异常:{ex.Message}\n{ex.StackTrace}");
                return NewReturnResult(1, preLog + $"发生了异常:{ex.Message}\n\n{ex.StackTrace}\n");
            }
        }
@@ -278,7 +278,7 @@
                .First(d => d.N_B_STATE == 2);
            if (detail == null) {
                LogHelper.Info($"{taskName}:AGV取货:查询明细单:当前没有执行中的明细单");
                LogHelper.Info($"{taskName}:AGV取货:查询明细单:当前没有执行中的明细单");
                return;
            }
@@ -286,23 +286,23 @@
                detail.N_B_STATE = spotStateCode;
                if (db.Updateable(detail).UpdateColumns(it => it.N_B_STATE).ExecuteCommand() <= 0) {
                    tran.RollbackTran();
                    LogHelper.Info($"{taskName}:AGV取货:修改明细单状态为【3任务执行完成】失败!");
                    LogHelper.Info($"{taskName}:AGV取货:修改明细单状态为【3任务执行完成】失败!");
                    return;
                }
                var finishedCount = db.Queryable<TN_Outbound_Detail>().Count(d => d.S_OO_NO == detail.S_OO_NO && d.N_B_STATE == 3);
                var allCount = db.Queryable<TN_Outbound_Detail>().Count(d => d.S_OO_NO == detail.S_OO_NO);
                LogHelper.Info($"{taskName}:AGV取货:统计任务已完成:{finishedCount}/{allCount}");
                LogHelper.Info($"{taskName}:AGV取货:统计任务已完成:{finishedCount}/{allCount}");
                if (finishedCount == allCount) { // 当前出库单下的所有明细单,任务都已经完成
                if (finishedCount == allCount) { // 当前出库单下的所有明细单,任务都已经完成
                    if (db.Updateable<TN_Outbound_Order>().SetColumns(it => it.N_B_STATE == 3)
                        .Where(it => it.S_NO == detail.S_OO_NO)
                        .ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        LogHelper.Info($"{taskName}:AGV取货:所有任务完成时:修改单据状态为3任务执行完成--失败!");
                        LogHelper.Info($"{taskName}:AGV取货:所有任务完成时:修改单据状态为3任务执行完成--失败!");
                        return;
                    }
                }
@@ -322,7 +322,7 @@
                    .First();
                if (plan == null) {
                    info = $"计划单号{task.S_BS_NO}不存在!";
                    info = $"计划单号{task.S_BS_NO}不存在!";
                    LogHelper.Info(info);
                }
@@ -331,13 +331,13 @@
                    .First();
                if (cgDetail == null) {
                    info = $"物料编码不存在!";
                    info = $"物料编码不存在!";
                    LogHelper.Info(info);
                }
                var model = new OtherModel.PickUpReturnErpInfo {
                    jhdh = plan.JHDH, // 计划单号(唯一标识)
                    ckzt = plan.CKZT, // 出库状态(需要返回)
                    jhdh = plan.JHDH, // 计划单号 (唯一标识)
                    ckzt = plan.CKZT, // 出库状态 (需要返回)
                    jhlb = plan.JHLB, // 计划类别
                    ckdh = plan.CKDH, // 参考单号
                    cph = plan.CPH, // 车牌号
@@ -348,9 +348,9 @@
                    cplb = plan.CPLB, // 产品类别
                    cplbmx = plan.CPLBMX, // 产品类别明细
                    pp = plan.PP, // 品牌
                    dj = plan.DJ, // 等级(需要返回)
                    dj = plan.DJ, // 等级 (需要返回)
                    gh = plan.GH, // 罐号
                    ph = plan.PH, // 批号(需要返回)
                    ph = plan.PH, // 批号 (需要返回)
                    bzlx = plan.BZLX, // 包装类型
                    pzdh = plan.PZDH, // 派装单号
                    pzd_dw = plan.PZD_DW, // 派装单单位
@@ -363,15 +363,15 @@
                    pz_zfrq = plan.PZ_ZFRQ, // 派装作废日期
                    pz_bz = plan.PZ_BZ, // 派装备注
                    ckdbh = plan.CKDBH, // 出库单编号
                    sfjs = plan.SFJS, // 实发件数(需要返回)--更新
                    sfsl = plan.SFSL, // 实发数量(需要返回)--更新
                    //sfcs = plan.SFCS , // 实发车数(需要返回)
                    //zcsj = plan.ZCSJ , // 装车时间(需要返回)
                    //jldw = plan.JLDW , // 计量单位(需要返回)
                    //fhrq = plan.FHRQ , // 发货日期(需要返回)
                    //ckdm = plan.CKDM , // 仓库代码(需要返回)
                    //fhr = plan.FHR , // 发货人(需要返回)
                    //czydm = plan.CZYDM , // 操作员(需要返回)
                    sfjs = plan.SFJS, // 实发件数 (需要返回) --更新
                    sfsl = plan.SFSL, // 实发数量 (需要返回) --更新
                    //sfcs = plan.SFCS , // 实发车数 (需要返回)
                    //zcsj = plan.ZCSJ , // 装车时间 (需要返回)
                    //jldw = plan.JLDW , // 计量单位 (需要返回)
                    //fhrq = plan.FHRQ , // 发货日期 (需要返回)
                    //ckdm = plan.CKDM , // 仓库代码 (需要返回)
                    //fhr = plan.FHR , // 发货人 (需要返回)
                    //czydm = plan.CZYDM , // 操作员 (需要返回)
                    shr_username = plan.SHR_USERNAME, // 审核人
                    shrq = plan.SHRQ, // 审核日期
                    zfbj = plan.ZFBJ, // 作废标记
@@ -380,14 +380,14 @@
                    shdw = plan.SHDW, // 收货单位
                    ysdw = plan.YSDW, // 运输单位
                    lxr = plan.LXR, // 联系人
                    //ry_zxg = plan.RY_ZXG , // 装卸工(需要返回)
                    //ry_ccsj = plan.RY_CCSJ , // 叉车司机(需要返回)
                    //ry_zxg = plan.RY_ZXG , // 装卸工 (需要返回)
                    //ry_ccsj = plan.RY_CCSJ , // 叉车司机 (需要返回)
                    erphx_jhdh = plan.ERPHX_JHDH, // erp交货单号
                    erphx_wlbm = plan.ERPHX_WLBM, // erp物料编码
                    erphx_wlmc = plan.ERPHX_WLMC, // erp物料名称
                    erphx_cjrq = plan.ERPHX_CJRQ, // erp创建日期
                    hw = plan.HW, // 货位(需要返回)
                    hwzt = plan.HWZT // 货位状态(需要返回)
                    hw = plan.HW, // 货位 (需要返回)
                    hwzt = plan.HWZT // 货位状态 (需要返回)
                };
                model.sfjs = cgDetail.N_ITEM_NUM;
                model.sfsl = (decimal) cgDetail.F_QTY; // TEMP
@@ -396,7 +396,7 @@
                var jsonInfo = JsonConvert.SerializeObject(model);
                var result = httpH.WebPost(Settings.ErpApiUrl + Settings.ErpRoute.PickUpReturn, jsonInfo);
                LogHelper.InfoApi($"取货完成反馈ERP接口,结果={result},调用参数:", model);
                LogHelper.InfoApi($"取货完成反馈ERP接口,结果={result},调用参数:", model);
                plan.SFJS = model.sfjs;
                plan.SFSL = model.sfsl;
@@ -411,14 +411,13 @@
        }
        /// <summary>
        /// 任务分发,根据调度类型发给不同的调度系统
        /// 任务分发,根据调度类型发给不同的调度系统
        /// </summary>
        internal static void Dispatch() {
            //查询任务
            //获取所有等待的任务
            var list = WCSHelper.GetWaitingTaskList();
            LogHelper.Info("等待任务信息" + JsonConvert.SerializeObject(list), "API");
            if (list.Count > 0) {
                LogHelper.Info($"轮询:任务分发:等待任务信息\n\n{JsonConvert.SerializeObject(list)}\n");
                list.ForEach(task => {
                    //使用自定义任务推送
                    //TaskProcess.SendTask(task);//调度NDC或杭奥或国自设备
@@ -426,7 +425,7 @@
                });
            }
            else {
                LogHelper.Info("暂无任务");
                LogHelper.Debug("轮询:任务分发:暂无任务");
            }
        }
@@ -436,7 +435,7 @@
            var ListenPort = Settings.TcpServerPort;
            TcpListener listener = new TcpListener(IPAddress.Any, ListenPort);
            listener.Start();
            Console.WriteLine($"后台服务已启动,监听端口 {ListenPort}...");
            Console.WriteLine($"后台服务已启动,监听端口 {ListenPort}...");
            while (true) {
                try {
@@ -444,7 +443,7 @@
                    System.Net.Sockets.TcpClient client = listener.AcceptTcpClient();
                    Console.WriteLine($"产线客户端已连接: {client.Client.RemoteEndPoint}");
                    // 处理客户端请求(使用Task避免阻塞主线程)
                    // 处理客户端请求 (使用Task避免阻塞主线程)
                    Task.Run(() => HandleClientRequest(client));
                }
                catch (Exception ex) {
@@ -465,15 +464,15 @@
                    // 解析消息
                    var message = JsonConvert.DeserializeObject<ProductCompletedMessage>(jsonMessage);
                    //Console.WriteLine($"收到产品完成通知 - 物料:{message.ItemCode};批次号:{message.BatchNo};容器号:{message.CntrCode}");
                    LogHelper.Info($"收到产品下线通知:"+message);
                    //Console.WriteLine($"收到产品完成通知 - 物料:{message.ItemCode};批次号:{message.BatchNo};容器号:{message.CntrCode}");
                    LogHelper.Info($"收到产品下线通知:"+message);
                    string startLocCode = "";
                    // A. 如果是单个产线PLC,通过TCP传播,需要传递产线ID,根据ID计算起点货位
                    //startLocCode = Settings.ProductionLines[message.Id].OffLoc[0]; // 理论上应该只有1个OffLoc,否则应该注明是哪一个
                    // A. 如果是单个产线PLC,通过TCP传播,需要传递产线ID,根据ID计算起点货位
                    //startLocCode = Settings.ProductionLines[message.Id].OffLoc[0]; // 理论上应该只有1个OffLoc,否则应该注明是哪一个
                    
                    // B. 如果是每条产线各一个客户端,更简单,直接根据IP地址确定终点货位
                    // B. 如果是每条产线各一个客户端,更简单,直接根据IP地址确定终点货位
                    // TODO 具体逻辑待后续确定时补完
                    var success = CreateInboundTask(startLocCode, message.CntrCode).Value;
@@ -490,7 +489,7 @@
        }
        /// <summary>
        /// 入库作业创建只接收起点货位和容器号(物料信息待定、通过产线号计算起点的逻辑放在方法外)
        /// 入库作业创建只接收起点货位和容器号 (物料信息待定,通过产线号计算起点的逻辑放在方法外)
        /// </summary>
        /// <param name="startLocCode"></param>
        /// <param name="cntrCode"></param>
@@ -514,7 +513,7 @@
                .First();
                if (startLoc == null) {
                    info = $"没有找到起点货位'{startLocCode}',或不满足要求:未上锁、当前容器数量=0";
                    info = $"没有找到起点货位'{startLocCode}',或不满足要求:未上锁,当前容器数量=0";
                    LogHelper.Info(info);
                    return new Result<bool>(false, info);
                }
@@ -533,13 +532,13 @@
                var endLoc = db.Queryable<TN_Location>()
                    .Where(a => endAreas.Contains(a.S_AREA_CODE))
                    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                    .Where(a => a.N_CURRENT_NUM == 0) // 筛选:空货位
                    .Where(a => a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") // 筛选:未上锁
                    .Where(a => a.N_CURRENT_NUM == 0) // 筛选:空货位
                    .OrderBy(l => l.N_LAYER)
                    .First();
                if (endLoc == null) {
                    info = $"没有找到合适的【终点货位】,需要满足要求:未上锁、当前容器数量=0";
                    info = $"没有找到合适的【终点货位】,需要满足要求:未上锁,当前容器数量=0";
                    LogHelper.Info(info);
                    return new Result<bool>(false, info);
                }
@@ -555,7 +554,7 @@
                        if (db.Deleteable(locCntrRelOld).ExecuteCommand() <= 0 &&
                            db.Updateable<TN_Location>().SetColumns(l => l.N_CURRENT_NUM == 0).Where(l => l.S_CODE == locCntrRelOld.S_LOC_CODE).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"删除旧货位容器关系表失败:货位编码{locCntrRelOld.S_LOC_CODE},容器编码{locCntrRelOld.S_CNTR_CODE}";
                            info = $"删除旧货位容器关系表失败:货位编码{locCntrRelOld.S_LOC_CODE},容器编码{locCntrRelOld.S_CNTR_CODE}";
                            LogHelper.Info(info);
                            return new Result<bool>(false, info);
                        }
@@ -563,7 +562,7 @@
                    if (db.Insertable(locCntrRel).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"插入货位容器关系表失败:货位编码{locCntrRel.S_LOC_CODE},容器编码{locCntrRel.S_CNTR_CODE}";
                        info = $"插入货位容器关系表失败:货位编码{locCntrRel.S_LOC_CODE},容器编码{locCntrRel.S_CNTR_CODE}";
                        LogHelper.Info(info);
                        return new Result<bool>(false, info);
                    }
@@ -573,10 +572,10 @@
                        it.S_LOCK_STATE,
                        it.S_LOCK_OP,
                        it.T_MODIFY,
                        it.N_CURRENT_NUM, // 起点货位绑定后,将货位状态更新
                        it.N_CURRENT_NUM, // 起点货位绑定后,将货位状态更新
                    }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成任务'{taskName}'失败:更新起点货位{startLoc.S_CODE}锁状态失败";
                        info = $"生成任务'{taskName}'失败:更新起点货位{startLoc.S_CODE}锁状态失败";
                        LogHelper.Info(info);
                        return new Result<bool>(false, info);
                    }
@@ -588,20 +587,20 @@
                        it.T_MODIFY,
                    }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成任务'{taskName}'失败:更新终点货位{endLoc.S_CODE}锁状态失败";
                        info = $"生成任务'{taskName}'失败:更新终点货位{endLoc.S_CODE}锁状态失败";
                        LogHelper.Info(info);
                        return new Result<bool>(false, info);
                    }
                    if (db.Insertable(task).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"生成任务'{taskName}'失败,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                        info = $"生成任务'{taskName}'失败,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                        LogHelper.Info(info);
                        return new Result<bool>(false, info);
                    }
                    tran.CommitTran();
                    info = $"生成任务'{taskName}'成功,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                    info = $"生成任务'{taskName}'成功,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                    LogHelper.Info(info);
                    return new Result<bool>(true, info);
                }
device/ModbusFactory.cs
@@ -21,28 +21,28 @@
        // 常用的Modbus功能码方法
        /// <summary>
        /// 读一个或多个线圈,返回一个bit真假数组
        /// 读一个或多个线圈,返回一个bit真假数组
        /// </summary>
        /// <param name="startingAddress"></param>
        /// <param name="quantity"></param>
        /// <returns></returns>
        bool[] ReadCoils(int startingAddress, int quantity);
        /// <summary>
        /// 读一个或多个离散输入,返回一个bit真假数组
        /// 读一个或多个离散输入,返回一个bit真假数组
        /// </summary>
        /// <param name="startingAddress"></param>
        /// <param name="quantity"></param>
        /// <returns></returns>
        bool[] ReadDiscreteInputs(int startingAddress, int quantity);
        /// <summary>
        /// 批量读取或单独读取保持寄存器,返回的是32位int数组
        /// 批量读取或单独读取保持寄存器,返回的是32位int数组
        /// </summary>
        /// <param name="startingAddress"></param>
        /// <param name="quantity"></param>
        /// <returns></returns>
        int[] ReadHoldingRegisters(int startingAddress, int quantity);
        /// <summary>
        /// 读一个或多个输入寄存器,返回一个int32位数组
        /// 读一个或多个输入寄存器,返回一个int32位数组
        /// </summary>
        /// <param name="startingAddress"></param>
        /// <param name="quantity"></param>
@@ -92,8 +92,8 @@
                case ModbusCommunicationType.TcpSocket:
                    return new TcpSocketCommunicator();
                default:
                    //throw new ArgumentException("不支持的Modbus通信方式:请选择EasyModbus或TcpSocket");
                    LogHelper.Info("不支持的Modbus通信方式:请选择EasyModbus或TcpSocket");
                    //throw new ArgumentException("不支持的Modbus通信方式:请选择EasyModbus或TcpSocket");
                    LogHelper.Info("不支持的Modbus通信方式:请选择EasyModbus或TcpSocket");
                    return null;
            }
        }
@@ -127,10 +127,10 @@
            _modbusClient.Connect();
            
            if (IsConnected) {
                LogHelper.Info($"连接成功:{ipAddress}:{port}");
                LogHelper.Info($"连接成功:{ipAddress}:{port}");
            }
            else {
                LogHelper.Info($"连接失败:{ipAddress}:{port}");
                LogHelper.Info($"连接失败:{ipAddress}:{port}");
            }
        }
@@ -143,10 +143,10 @@
            _modbusClient?.Disconnect();
            if (IsConnected) {
                LogHelper.Info($"连接成功:{_modbusClient.IPAddress}:{_modbusClient.Port}");
                LogHelper.Info($"连接成功:{_modbusClient.IPAddress}:{_modbusClient.Port}");
            }
            else {
                LogHelper.Info($"连接失败:{_modbusClient.IPAddress} : {_modbusClient.Port}");
                LogHelper.Info($"连接失败:{_modbusClient.IPAddress} : {_modbusClient.Port}");
            }
        }
@@ -169,11 +169,11 @@
                    res = _modbusClient.ReadCoils(startingAddress, quantity);
                    if (res.Length != 0) {
                        //读取成功
                        LogHelper.Info($"读取成功:ReadCoils:IP:{ip},Port:{port}");
                        LogHelper.Info($"读取成功:ReadCoils:IP:{ip},Port:{port}");
                    }
                    else {
                        //读取失败
                        LogHelper.Info($"读取失败:ReadCoils:IP:{ip},Port:{port}");
                        LogHelper.Info($"读取失败:ReadCoils:IP:{ip},Port:{port}");
                    }
                }
                catch (Exception ex) {
@@ -181,7 +181,7 @@
                }
            }
            else {
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
            }
            return res;
        }
@@ -196,11 +196,11 @@
                    res = _modbusClient.ReadDiscreteInputs(startingAddress, quantity);
                    if (res.Length != 0) {
                        //读取成功
                        LogHelper.Info($"读取成功:ReadDiscreteInputs:IP:{ip},Port:{port}");
                        LogHelper.Info($"读取成功:ReadDiscreteInputs:IP:{ip},Port:{port}");
                    }
                    else {
                        //读取失败
                        LogHelper.Info($"读取失败:ReadDiscreteInputs:IP:{ip},Port:{port}");
                        LogHelper.Info($"读取失败:ReadDiscreteInputs:IP:{ip},Port:{port}");
                    }
                }
                catch (Exception ex) {
@@ -208,7 +208,7 @@
                }
            }
            else {
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
            }
            return res;
        }
@@ -220,24 +220,24 @@
                var ip = client.IPAddress;
                var port = client.Port;
                try {
                    //一个寄存器是16位,返回2个int类型
                    //一个寄存器是16位,返回2个int类型
                    res = client.ReadHoldingRegisters(startingAddress, quantity);
                    if (res.Length != 0) {
                        //读取成功
                        LogHelper.Info($"读取成功:ReadHoldingRegisters:IP:{ip},Port:{port}");
                        LogHelper.Info($"读取成功:ReadHoldingRegisters:IP:{ip},Port:{port}");
                    }
                    else {
                        //读取失败
                        LogHelper.Info($"读取失败:ReadHoldingRegisters:IP:{ip},Port:{port}");
                        LogHelper.Info($"读取失败:ReadHoldingRegisters:IP:{ip},Port:{port}");
                    }
                }
                catch (Exception ex) {
                    //如果请求数量超出保持寄存器的最大数据行数,会报错
                    //如果请求数量超出保持寄存器的最大数据行数,会报错
                    LogHelper.InfoEx(ex);
                }
            }
            else {
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
            }
            return res;
        }
@@ -252,11 +252,11 @@
                    res = client.ReadInputRegisters(startingAddress, quantity);
                    if (res.Length != 0) {
                        //读取成功
                        LogHelper.Info($"读取成功:ReadInputRegisters:IP:{ip},Port:{port}");
                        LogHelper.Info($"读取成功:ReadInputRegisters:IP:{ip},Port:{port}");
                    }
                    else {
                        //读取失败
                        LogHelper.Info($"读取失败:ReadInputRegisters:IP:{ip},Port:{port}");
                        LogHelper.Info($"读取失败:ReadInputRegisters:IP:{ip},Port:{port}");
                    }
                }
                catch (Exception ex) {
@@ -264,7 +264,7 @@
                }
            }
            else {
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
            }
            return res;
        }
@@ -280,11 +280,11 @@
                    res = value == client.ReadCoils(coilAddress, 1)[0];
                    if (res) {
                        //写入成功
                        LogHelper.Info($"写入成功:WriteSingleCoil:IP:{ip},Port:{port}");
                        LogHelper.Info($"写入成功:WriteSingleCoil:IP:{ip},Port:{port}");
                    }
                    else {
                        //写入失败
                        LogHelper.Info($"写入失败:WriteSingleCoil:IP:{ip},Port:{port}");
                        LogHelper.Info($"写入失败:WriteSingleCoil:IP:{ip},Port:{port}");
                    }
                }
                catch (Exception ex) {
@@ -292,7 +292,7 @@
                }
            }
            else {
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
            }
            return res;
        }
@@ -308,11 +308,11 @@
                    res = value == client.ReadHoldingRegisters(registerAddress, 1)[0];
                    if (res) {
                        //写入成功
                        LogHelper.Info($"写入成功:WriteSingleRegister:IP:{ip},Port:{port}");
                        LogHelper.Info($"写入成功:WriteSingleRegister:IP:{ip},Port:{port}");
                    }
                    else {
                        //写入失败
                        LogHelper.Info($"写入失败:WriteSingleRegister:IP:{ip},Port:{port}");
                        LogHelper.Info($"写入失败:WriteSingleRegister:IP:{ip},Port:{port}");
                    }
                }
                catch (Exception ex) {
@@ -320,7 +320,7 @@
                }
            }
            else {
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
            }
            return res;
        }
@@ -337,11 +337,11 @@
                    res = values.SequenceEqual(dataRead);
                    if (res) {
                        //写入成功
                        LogHelper.Info($"写入成功:WriteMultipleCoils:IP:{ip},Port:{port}");
                        LogHelper.Info($"写入成功:WriteMultipleCoils:IP:{ip},Port:{port}");
                    }
                    else {
                        //写入失败
                        LogHelper.Info($"写入失败:WriteMultipleCoils:IP:{ip},Port:{port}");
                        LogHelper.Info($"写入失败:WriteMultipleCoils:IP:{ip},Port:{port}");
                    }
                }
                catch (Exception ex) {
@@ -349,7 +349,7 @@
                }
            }
            else {
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
            }
            return res;
        }
@@ -377,7 +377,7 @@
                }
            }
            else {
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
                LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
            }
            return res;
        }
@@ -421,16 +421,16 @@
            if (quantity < 1 || quantity > 2000)
                throw new ArgumentException("Quantity must be between 1 and 2000");
            // 构建请求报文(12字节)
            // 构建请求报文 (12字节)
            byte[] request = new byte[12];
            request[0] = 0x00; // 事务ID高字节
            request[1] = 0x01; // 事务ID低字节(示例值)
            request[2] = 0x00; // 协议ID高字节(固定0)
            request[3] = 0x00; // 协议ID低字节(固定0)
            request[4] = 0x00; // 长度高字节(后续6字节)
            request[1] = 0x01; // 事务ID低字节 (示例值)
            request[2] = 0x00; // 协议ID高字节 (固定0)
            request[3] = 0x00; // 协议ID低字节 (固定0)
            request[4] = 0x00; // 长度高字节 (后续6字节)
            request[5] = 0x06; // 长度低字节
            request[6] = unitIdentifier; // 单元标识符
            request[7] = 0x01; // 功能码(读线圈)
            request[7] = 0x01; // 功能码 (读线圈)
            request[8] = (byte)(startingAddress >> 8); // 起始地址高字节
            request[9] = (byte)startingAddress; // 起始地址低字节
            request[10] = (byte)(quantity >> 8); // 数量高字节
@@ -439,19 +439,19 @@
            // 发送请求
            _networkStream.Write(request, 0, request.Length);
            // 读取响应头(8字节)
            // 读取响应头 (8字节)
            byte[] responseHeader = new byte[8];
            int bytesRead = _networkStream.Read(responseHeader, 0, 8);
            if (bytesRead != 8)
                throw new Exception("Invalid response header length");
            // 校验事务ID、协议ID、单元标识符
            // 校验事务ID,协议ID,单元标识符
            if (responseHeader[0] != request[0] || responseHeader[1] != request[1] ||
                responseHeader[2] != 0x00 || responseHeader[3] != 0x00 ||
                responseHeader[6] != unitIdentifier)
                throw new Exception("Invalid response header");
            // 检查异常响应(功能码 + 0x80)
            // 检查异常响应 (功能码 + 0x80)
            if (responseHeader[7] == 0x81) {
                int errorCode = _networkStream.ReadByte();
                throw new Exception($"Modbus error. Code: {errorCode}");
@@ -459,14 +459,14 @@
            else if (responseHeader[7] != 0x01)
                throw new Exception("Invalid function code in response");
            // 读取数据部分(字节数 + 实际数据)
            // 读取数据部分 (字节数 + 实际数据)
            byte byteCount = responseHeader[8];
            byte[] responseData = new byte[byteCount];
            bytesRead = _networkStream.Read(responseData, 0, byteCount);
            if (bytesRead != byteCount)
                throw new Exception("Invalid response data length");
            // 解析线圈状态(每个位表示一个线圈)
            // 解析线圈状态 (每个位表示一个线圈)
            bool[] coils = new bool[quantity];
            for (int i = 0; i < quantity; i++) {
                int byteIndex = i / 8;
@@ -478,16 +478,16 @@
        }
        public bool[] ReadDiscreteInputs(int startingAddress, int quantity) {
            // 报文结构与ReadCoils几乎相同,仅功能码改为0x02
            // 报文结构与ReadCoils几乎相同,仅功能码改为0x02
            byte[] request = new byte[12];
            request[0] = 0x00; // 事务ID高字节
            request[1] = 0x01; // 事务ID低字节(示例值)
            request[2] = 0x00; // 协议ID高字节(固定0)
            request[3] = 0x00; // 协议ID低字节(固定0)
            request[4] = 0x00; // 长度高字节(后续6字节)
            request[1] = 0x01; // 事务ID低字节 (示例值)
            request[2] = 0x00; // 协议ID高字节 (固定0)
            request[3] = 0x00; // 协议ID低字节 (固定0)
            request[4] = 0x00; // 长度高字节 (后续6字节)
            request[5] = 0x06; // 长度低字节
            request[6] = unitIdentifier;
            request[7] = 0x02; // 功能码:读离散输入
            request[7] = 0x02; // 功能码:读离散输入
            request[8] = (byte)(startingAddress >> 8); // 起始地址高字节
            request[9] = (byte)startingAddress; // 起始地址低字节
            request[10] = (byte)(quantity >> 8); // 数量高字节
@@ -499,7 +499,7 @@
            byte[] responseHeader = new byte[8];
            _networkStream.Read(responseHeader, 0, 8);
            // 校验和异常处理(参考ReadCoils)
            // 校验和异常处理 (参考ReadCoils)
            if (responseHeader[7] == 0x82) // 异常响应
            {
                int errorCode = _networkStream.ReadByte();
@@ -525,16 +525,16 @@
            if (quantity < 1 || quantity > 125)
                throw new ArgumentException("Quantity must be between 1 and 125");
            // 构建请求报文(12字节)
            // 构建请求报文 (12字节)
            byte[] request = new byte[12];
            request[0] = 0x00; // 事务ID高字节(示例值)
            request[0] = 0x00; // 事务ID高字节 (示例值)
            request[1] = 0x01; // 事务ID低字节
            request[2] = 0x00; // 协议ID高字节(固定0)
            request[3] = 0x00; // 协议ID低字节(固定0)
            request[4] = 0x00; // 长度高字节(后续6字节)
            request[2] = 0x00; // 协议ID高字节 (固定0)
            request[3] = 0x00; // 协议ID低字节 (固定0)
            request[4] = 0x00; // 长度高字节 (后续6字节)
            request[5] = 0x06; // 长度低字节
            request[6] = unitIdentifier; // 单元标识符
            request[7] = 0x03; // 功能码(读保持寄存器)
            request[7] = 0x03; // 功能码 (读保持寄存器)
            request[8] = (byte)(startingAddress >> 8); // 起始地址高字节
            request[9] = (byte)startingAddress; // 起始地址低字节
            request[10] = (byte)(quantity >> 8); // 数量高字节
@@ -543,19 +543,19 @@
            // 发送请求
            _networkStream.Write(request, 0, request.Length);
            // 读取响应头(8字节)
            // 读取响应头 (8字节)
            byte[] responseHeader = new byte[8];
            int bytesRead = _networkStream.Read(responseHeader, 0, 8);
            if (bytesRead != 8)
                throw new Exception("Invalid response header length");
            // 校验事务ID、协议ID、单元标识符
            // 校验事务ID,协议ID,单元标识符
            if (responseHeader[0] != request[0] || responseHeader[1] != request[1] ||
                responseHeader[2] != 0x00 || responseHeader[3] != 0x00 ||
                responseHeader[6] != unitIdentifier)
                throw new Exception("Invalid response header");
            // 检查异常响应(功能码 + 0x80)
            // 检查异常响应 (功能码 + 0x80)
            if (responseHeader[7] == 0x83) {
                int errorCode = _networkStream.ReadByte();
                throw new Exception($"Modbus error. Code: {errorCode}");
@@ -563,14 +563,14 @@
            else if (responseHeader[7] != 0x03)
                throw new Exception("Invalid function code in response");
            // 读取数据部分(字节数 + 实际数据)
            // 读取数据部分 (字节数 + 实际数据)
            byte byteCount = responseHeader[8];
            byte[] responseData = new byte[byteCount];
            bytesRead = _networkStream.Read(responseData, 0, byteCount);
            if (bytesRead != byteCount)
                throw new Exception("Invalid response data length");
            // 解析寄存器值(大端序)
            // 解析寄存器值 (大端序)
            int[] registers = new int[quantity];
            for (int i = 0; i < quantity; i++) {
                int offset = i * 2;
@@ -587,22 +587,22 @@
            // 构建Modbus TCP请求报文
            byte[] request = new byte[12];
            // 事务标识符(可以简单递增)
            // 事务标识符 (可以简单递增)
            request[0] = 0x00;
            request[1] = 0x01;
            // 协议标识符(Modbus固定为0)
            // 协议标识符 (Modbus固定为0)
            request[2] = 0x00;
            request[3] = 0x00;
            // 长度字段(后面还有6字节)
            // 长度字段 (后面还有6字节)
            request[4] = 0x00;
            request[5] = 0x06;
            // 单元标识符
            request[6] = unitIdentifier;
            // 功能码(4表示读输入寄存器)
            // 功能码 (4表示读输入寄存器)
            request[7] = 0x04;
            // 起始地址
@@ -677,7 +677,7 @@
            _networkStream.Write(request, 0, request.Length);
            // 响应应与请求完全一致(回显)
            // 响应应与请求完全一致 (回显)
            byte[] response = new byte[12];
            int bytesRead = _networkStream.Read(response, 0, 12);
            if (bytesRead != 12)
@@ -708,7 +708,7 @@
            _networkStream.Write(request, 0, request.Length);
            // 检查回显响应(同WriteSingleCoil)
            // 检查回显响应 (同WriteSingleCoil)
            byte[] response = new byte[12];
            _networkStream.Read(response, 0, 12);
            if (!response.SequenceEqual(request))
@@ -722,7 +722,7 @@
            if (quantity < 1 || quantity > 1968)
                throw new ArgumentException("Quantity must be between 1 and 1968");
            // 计算需要的字节数(每个字节存储8个线圈状态)
            // 计算需要的字节数 (每个字节存储8个线圈状态)
            int byteCount = (quantity + 7) / 8;
            byte[] coilBytes = new byte[byteCount];
@@ -735,7 +735,7 @@
                }
            }
            // 构建请求报文(13 + 线圈字节数)
            // 构建请求报文 (13 + 线圈字节数)
            byte[] request = new byte[13 + coilBytes.Length];
            request[0] = 0x00; // 事务ID高字节
            request[1] = 0x01; // 事务ID低字节
@@ -744,7 +744,7 @@
            request[4] = (byte)((7 + coilBytes.Length) >> 8); // 长度高字节
            request[5] = (byte)(7 + coilBytes.Length); // 长度低字节
            request[6] = unitIdentifier;
            request[7] = 0x0F; // 功能码(写多个线圈)
            request[7] = 0x0F; // 功能码 (写多个线圈)
            request[8] = (byte)(startingAddress >> 8); // 起始地址高字节
            request[9] = (byte)startingAddress; // 起始地址低字节
            request[10] = (byte)(quantity >> 8); // 数量高字节
@@ -755,7 +755,7 @@
            // 发送请求
            _networkStream.Write(request, 0, request.Length);
            // 读取响应(固定12字节)
            // 读取响应 (固定12字节)
            byte[] response = new byte[12];
            int bytesRead = _networkStream.Read(response, 0, 12);
            if (bytesRead != 12)
@@ -774,14 +774,14 @@
            if (quantity < 1 || quantity > 123)
                throw new ArgumentException("Quantity must be between 1 and 123");
            // 将ushort数组转换为字节数组(大端序)
            // 将ushort数组转换为字节数组 (大端序)
            byte[] valueBytes = new byte[quantity * 2];
            for (int i = 0; i < quantity; i++) {
                valueBytes[i * 2] = (byte)(values[i] >> 8);
                valueBytes[i * 2 + 1] = (byte)values[i];
            }
            // 构建请求报文(13 + 值字节数)
            // 构建请求报文 (13 + 值字节数)
            byte[] request = new byte[13 + valueBytes.Length];
            request[0] = 0x00; // 事务ID高字节
            request[1] = 0x01; // 事务ID低字节
@@ -790,7 +790,7 @@
            request[4] = (byte)((7 + valueBytes.Length) >> 8); // 长度高字节
            request[5] = (byte)(7 + valueBytes.Length); // 长度低字节
            request[6] = unitIdentifier;
            request[7] = 0x10; // 功能码(写多个寄存器)
            request[7] = 0x10; // 功能码 (写多个寄存器)
            request[8] = (byte)(startingAddress >> 8); // 起始地址高字节
            request[9] = (byte)startingAddress; // 起始地址低字节
            request[10] = (byte)(quantity >> 8); // 数量高字节
@@ -801,7 +801,7 @@
            // 发送请求
            _networkStream.Write(request, 0, request.Length);
            // 读取响应(固定12字节)
            // 读取响应 (固定12字节)
            byte[] response = new byte[12];
            int bytesRead = _networkStream.Read(response, 0, 12);
            if (bytesRead != 12)
device/ModbusHelper.cs
@@ -10,7 +10,7 @@
    /// modbus tcp 用第三方的包
    /// </summary>
    public class ModbusHelper {
        // 内存,连接上的 Modbus 通讯对象
        // 内存,连接上的 Modbus 通讯对象
        private static Dictionary<string, ModbusClient> _ipPort_ModbusClient = new Dictionary<string, ModbusClient>();
        public ModbusHelper(string ip, int port = 502, byte slaveId = 1) {
@@ -25,7 +25,7 @@
            try {
                if (ip == null || ip.Trim() == "") {
                    // 读取配置信息失败
                    LogHelper.Info("Modbus:读取配置信息失败");
                    LogHelper.Info("Modbus:读取配置信息失败");
                    return;
                }
@@ -59,7 +59,7 @@
        }
        /// <summary>
        /// 连接 Modbus 下位机(私有,内部调用)
        /// 连接 Modbus 下位机 (私有,内部调用)
        /// </summary>
        /// <param name="modbusClient"></param>
        private static void Link(ModbusClient modbusClient) {
@@ -79,7 +79,7 @@
        }
        /// <summary>
        /// 读一个或多个线圈,返回一个bit真假数组
        /// 读一个或多个线圈,返回一个bit真假数组
        /// </summary>
        /// <param name="address"></param>
        /// <param name="qty"></param>
@@ -131,7 +131,7 @@
                    }
                }
                catch (Exception ex) {
                    LogHelper.InfoEx(ex);
                }
            }
            else {
@@ -162,7 +162,7 @@
                    }
                }
                catch (Exception ex) {
                    LogHelper.InfoEx(ex);
                }
            }
            else {
@@ -173,7 +173,7 @@
        }
        /// <summary>
        /// 批量读取或单独读取保持寄存器,返回的是32位int数组
        /// 批量读取或单独读取保持寄存器,返回的是32位int数组
        /// </summary>
        /// <param name="address">读取起始位</param>
        /// <param name="qty">读取的数量</param>
@@ -185,7 +185,7 @@
            var client = GetModbusClient(ip, port);
            if (client != null && client.Connected) {
                try {
                    //一个寄存器是16位,返回2个int类型
                    //一个寄存器是16位,返回2个int类型
                    res = client.ReadHoldingRegisters(address, qty);
                    if (res.Length != 0) {
                        //读取成功
@@ -195,7 +195,7 @@
                    }
                }
                catch (Exception ex) {
                    //如果请求数量超出保持寄存器的最大数据行数,会报错
                    //如果请求数量超出保持寄存器的最大数据行数,会报错
                    LogHelper.Info($"发生了异常:{ex.Message},IP:{ip},Port:{port}", "Error");
                }
            }
@@ -227,7 +227,7 @@
                    }
                }
                catch (Exception ex) {
                    LogHelper.InfoEx(ex);
                }
            }
            else {
@@ -270,7 +270,7 @@
        }
        /// <summary>
        /// 读一个或多个离散输入,返回一个bit真假数组
        /// 读一个或多个离散输入,返回一个bit真假数组
        /// </summary>
        /// <param name="address"></param>
        /// <param name="qty"></param>
@@ -291,7 +291,7 @@
                    }
                }
                catch (Exception ex) {
                    LogHelper.InfoEx(ex);
                }
            }
            else {
@@ -302,7 +302,7 @@
        }
        /// <summary>
        /// 读一个或多个输入寄存器,返回一个int32位数组
        /// 读一个或多个输入寄存器,返回一个int32位数组
        /// </summary>
        /// <param name="address"></param>
        /// <param name="qty"></param>
@@ -323,7 +323,7 @@
                    }
                }
                catch (Exception ex) {
                    LogHelper.InfoEx(ex);
                }
            }
            else {
@@ -338,7 +338,7 @@
        }
        #region Modbus 协议读写字符串
        // 将字符串转换为 Modbus 寄存器数组(int[] 形式,每个int存一个16位值)
        // 将字符串转换为 Modbus 寄存器数组 (int[] 形式,每个int存一个16位值)
        private static int[] StringToRegisters(string text) {
            // 填充为偶数长度
            if (text.Length % 2 != 0) {
@@ -349,7 +349,7 @@
            int[] registers = new int[bytes.Length / 2];
            for (int i = 0; i < registers.Length; i++) {
                // 大端序:高位字节在前
                // 大端序:高位字节在前
                registers[i] = (bytes[i * 2] << 8) | bytes[i * 2 + 1];
            }
@@ -361,7 +361,7 @@
            byte[] bytes = new byte[registers.Length * 2];
            for (int i = 0; i < registers.Length; i++) {
                // 提取低16位(忽略高16位)
                // 提取低16位 (忽略高16位)
                ushort registerValue = (ushort)(registers[i] & 0xFFFF);
                // 解析大端序
@@ -376,7 +376,7 @@
        #endregion
        /// <summary>
        /// 获取 Modbus 通讯对象(私有,内部调用)
        /// 获取 Modbus 通讯对象 (私有,内部调用)
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
device/OpcUaHelper.cs
@@ -22,7 +22,7 @@
        {
            try
            {
                // 创建一个应用配置对象,用于设置应用名称、唯一标识、类型、证书和安全策略
                // 创建一个应用配置对象,用于设置应用名称,唯一标识,类型,证书和安全策略
                var config = new ApplicationConfiguration()
                {
                    ApplicationName = "MyClient",
@@ -48,18 +48,18 @@
                // 验证应用配置对象
                await config.Validate(ApplicationType.Client);
                // 设置证书验证事件,用于自动接受不受信任的证书
                // 设置证书验证事件,用于自动接受不受信任的证书
                if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
                {
                    config.CertificateValidator.CertificateValidation += (s, e) => { e.Accept = (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted); };
                }
                // 创建一个应用实例对象,用于检查证书
                // 创建一个应用实例对象,用于检查证书
                var application = new ApplicationInstance(config);
                // 检查应用实例对象的证书
                bool check = await application.CheckApplicationInstanceCertificate(false, 2048);
                // 创建一个会话对象,用于连接到 OPC UA 服务器
                // 创建一个会话对象,用于连接到 OPC UA 服务器
                EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint("opc.tcp://172.16.57.41:4840", true);
                EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(config);
                ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);
device/ProductionLineDevice.cs
@@ -32,32 +32,32 @@
        public string Id { get; set; }
        /// <summary>
        /// 系统状态:0本地 1联动(AGV模式) 2故障
        /// 系统状态:0本地 1联动(AGV模式) 2故障
        /// </summary>
        public int SystemState { get; set; }
        /// <summary>
        /// 满垛下线:1输送线末端有成品料,需要AGV搬运 0默认值
        /// 满垛下线:1输送线末端有成品料,需要AGV搬运 0默认值
        /// </summary>
        public int FullOffline { get; set; }
        /// <summary>
        /// 呼叫托盘垛:1需要空托,呼叫AGV配送 0默认值
        /// 呼叫托盘垛:1需要空托,呼叫AGV配送 0默认值
        /// </summary>
        public int CallPallet { get; set; }
        /// <summary>
        /// 允许 AGV 放托盘垛:1允许放垛 0默认值
        /// 允许 AGV 放托盘垛:1允许放垛 0默认值
        /// </summary>
        public int AllowAgvPlacePallet { get; set; }
        /// <summary>
        /// AGV 正在取货:下线AGV写入1,取货完成后恢复0
        /// AGV 正在取货:下线AGV写入1,取货完成后恢复0
        /// </summary>
        public int AgvPicking { get; set; }
        /// <summary>
        /// AGV 正在取货:下线AGV写入1,取货完成后恢复0
        /// AGV 正在取货:下线AGV写入1,取货完成后恢复0
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
@@ -71,12 +71,12 @@
        }
        /// <summary>
        /// AGV 正在放托盘垛:上线AGV写入1,放托完成后恢复0
        /// AGV 正在放托盘垛:上线AGV写入1,放托完成后恢复0
        /// </summary>
        public int AgvPlacingPallet { get; set; }
        /// <summary>
        /// AGV 正在放托盘垛:上线AGV写入1,放托完成后恢复0
        /// AGV 正在放托盘垛:上线AGV写入1,放托完成后恢复0
        /// </summary>
        public bool SetAgvPlacingPallet(int value) {
            if (!ModbusHelper.WriteSingleRegister(11, value, Ip, Port)) {
device/S7Helper.cs
@@ -18,7 +18,7 @@
    /// </summary>
    public class S7Helper
    {
        public static Dictionary<string, Plc> ip_Plc = new Dictionary<string, Plc>();//内存,连接上的PLC通讯对象
        public static Dictionary<string, Plc> ip_Plc = new Dictionary<string, Plc>();//内存,连接上的PLC通讯对象
        public S7Helper(string ip, short rack, short slot)
        {
@@ -93,12 +93,12 @@
        }
        /// <summary>
        /// 批量读取或单独读取DB块数据(8位byte),并转换成字符串形式
        /// 批量读取或单独读取DB块数据 (8位byte) ,并转换成字符串形式
        /// </summary>
        /// <param name="deviceIp">plc设备通讯地址</param>
        /// <param name="dbNo">DB块号</param>
        /// <param name="startByteAdr">起始byte地址,最小值0,1=8位=1个Block</param>
        /// <param name="count">读取的个数,1个=8位十六进制数</param>
        /// <param name="startByteAdr">起始byte地址,最小值0,1=8位=1个Block</param>
        /// <param name="count">读取的个数,1个=8位十六进制数</param>
        /// <returns></returns>
        public static string ReadString(string deviceIp, int dbNo, int startByteAdr, int count)
        {
@@ -111,15 +111,15 @@
                    if (plc.IsConnected)
                    {
                        var data = plc.ReadBytes(DataType.DataBlock, dbNo, startByteAdr, count);
                        result = System.Text.Encoding.UTF8.GetString(data).TrimEnd('\0').TrimEnd('\n').TrimEnd('\r'); ;//此方法可以把byte数组转换成字符串,但是会造成\0\u结束符不显示,需要下位机正确的数据
                        result = System.Text.Encoding.UTF8.GetString(data).TrimEnd('\0').TrimEnd('\n').TrimEnd('\r'); ;//此方法可以把byte数组转换成字符串,但是会造成\0\u结束符不显示,需要下位机正确的数据
                        if (result == string.Empty)
                        {
                            Link(plc);//设备发送的数据为空,重连
                            Link(plc);//设备发送的数据为空,重连
                        }
                    }
                    else
                    {
                        Link(plc);//设备未连接,重连
                        Link(plc);//设备未连接,重连
                    }
                }
                else
@@ -136,11 +136,11 @@
        }
        /// <summary>
        /// 批量写入或单独写入DB块数据(8位byte),以字符串转换byte形式写入
        /// 批量写入或单独写入DB块数据 (8位byte) ,以字符串转换byte形式写入
        /// </summary>
        /// <param name="deviceIp">plc设备通讯地址</param>
        /// <param name="dbNo">DB块号</param>
        /// <param name="startByteAdr">起始byte地址,最小值0,1=8位=1个Block</param>
        /// <param name="startByteAdr">起始byte地址,最小值0,1=8位=1个Block</param>
        /// <param name="data">要写入的数据</param>
        /// <returns></returns>
        public static bool WriteString(string deviceIp, int dbNo, int startByteAdr, string data)
@@ -165,7 +165,7 @@
                    }
                    else
                    {
                        Link(plc);//设备未连接,重连
                        Link(plc);//设备未连接,重连
                    }
                }
                else
@@ -182,12 +182,12 @@
        }
        /// <summary>
        /// 批量读取或单独读取DB块数据(8位byte)
        /// 批量读取或单独读取DB块数据 (8位byte)
        /// </summary>
        /// <param name="deviceIp">plc设备通讯地址</param>
        /// <param name="dbNo">DB块号</param>
        /// <param name="startByteAdr">起始byte地址,最小值0,1=8位=1个Block</param>
        /// <param name="count">读取的个数,1个=8位十六进制数</param>
        /// <param name="startByteAdr">起始byte地址,最小值0,1=8位=1个Block</param>
        /// <param name="count">读取的个数,1个=8位十六进制数</param>
        /// <returns></returns>
        public static byte[] ReadBytes(string deviceIp, int dbNo, int startByteAdr, int count)
        {
@@ -203,12 +203,12 @@
                        if (result.Length == 0)
                        {
                            Link(plc);//设备发送的数据为空,重连
                            Link(plc);//设备发送的数据为空,重连
                        }
                    }
                    else
                    {
                        Link(plc);//设备未连接,重连
                        Link(plc);//设备未连接,重连
                    }
                }
                else
@@ -225,11 +225,11 @@
        }
        /// <summary>
        /// 批量写入或单独写入DB块数据(8位byte)
        /// 批量写入或单独写入DB块数据 (8位byte)
        /// </summary>
        /// <param name="deviceIp">plc设备通讯地址</param>
        /// <param name="dbNo">DB块号</param>
        /// <param name="startByteAdr">起始byte地址,最小值0,1=8位=1个Block</param>
        /// <param name="startByteAdr">起始byte地址,最小值0,1=8位=1个Block</param>
        /// <param name="data">要写入的数据</param>
        /// <returns></returns>
        public static bool WriteBytes(string deviceIp, int dbNo, int startByteAdr, byte[] data)
@@ -253,7 +253,7 @@
                    }
                    else
                    {
                        Link(plc);//设备未连接,重连
                        Link(plc);//设备未连接,重连
                    }
                }
                else
@@ -270,11 +270,11 @@
        }
        /// <summary>
        /// 批量读取或单独读取DB块数据(1位bit)
        /// 批量读取或单独读取DB块数据 (1位bit)
        /// </summary>
        /// <param name="deviceIp">plc设备通讯地址</param>
        /// <param name="dbNo">DB块号</param>
        /// <param name="startByteAdr">起始byte地址,最小值0,1=8位=1个Block</param>
        /// <param name="startByteAdr">起始byte地址,最小值0,1=8位=1个Block</param>
        /// <param name="count">要读取多少位</param>
        /// <param name="bitAdr">从第几位开始读取</param>
        /// <returns></returns>
@@ -299,12 +299,12 @@
                        }
                        if (result.Length == 0)
                        {
                            Link(plc);//设备发送的数据为空,重连
                            Link(plc);//设备发送的数据为空,重连
                        }
                    }
                    else
                    {
                        Link(plc);//设备未连接,重连
                        Link(plc);//设备未连接,重连
                    }
                }
                else
@@ -321,12 +321,12 @@
        }
        /// <summary>
        /// 批量写入或单独写入DB块数据(1位bit)
        /// 批量写入或单独写入DB块数据 (1位bit)
        /// </summary>
        /// <param name="deviceIp">plc设备通讯地址</param>
        /// <param name="dbNo">DB块号</param>
        /// <param name="startByteAdr">起始byte地址,最小值0,1=8位=1个Block</param>
        /// <param name="biteAdr">起始bit地址,从第几位开始写k</param>
        /// <param name="startByteAdr">起始byte地址,最小值0,1=8位=1个Block</param>
        /// <param name="biteAdr">起始bit地址,从第几位开始写k</param>
        /// <param name="bitValue">要写入的数据</param>
        /// <returns></returns>
        public static bool WriteBits(string deviceIp, int dbNo, int startByteAdr, byte biteAdr, BitArray bitValue)
@@ -353,7 +353,7 @@
                    }
                    else
                    {
                        Link(plc);//设备未连接,重连
                        Link(plc);//设备未连接,重连
                    }
                }
                else
@@ -409,7 +409,7 @@
        }
        /// <summary>
        /// 通过S7协议连接下位机时需要的model,应该放在model层,这里我懒了
        /// 通过S7协议连接下位机时需要的model,应该放在model层,这里我懒了
        /// </summary>
        public class S7ConfigModel
        {
device/TcpClient.cs
@@ -32,7 +32,7 @@
                    res = BitConverter.ToString(data).Replace("-", "");
                }
                catch (Exception ex) {
                    LogHelper.Error(ex.Message, ex);
                    LogHelper.InfoEx(ex);
                }
                client.Disconnect(true);
                client.Dispose();
@@ -42,7 +42,7 @@
        }
        /// <summary>
        /// 读保持寄存器,modbus rtu的封装
        /// 读保持寄存器,modbus rtu的封装
        /// </summary>
        /// <param name="address"></param>
        /// <param name="qty"></param>
device/TcpClientHelper.cs
@@ -26,15 +26,15 @@
        public static bool Init(string ip, int port) {
            lock (_connectLock) {
                try {
                    // 若正在连接中,直接返回
                    // 若正在连接中,直接返回
                    if (_isConnecting) {
                        LogHelper.Info("已有连接正在尝试中,禁止重复操作");
                        LogHelper.Info("已有连接正在尝试中,禁止重复操作");
                        return false;
                    }
                    _isConnecting = true; // 标记为连接中
                    // 释放旧 Socket(仅在未连接时)
                    // 释放旧 Socket (仅在未连接时)
                    if (_clientSocket != null && !_clientSocket.Connected) {
                        SafeCloseSocket();
                    }
@@ -52,7 +52,7 @@
                }
                catch (SocketException ex) {
                    _isConnecting = false;
                    LogHelper.Error($"初始化连接失败: {ex.Message}", ex);
                    LogHelper.InfoEx(ex);
                    return false;
                }
            }
@@ -64,7 +64,7 @@
            lock (_linkLock) {
                try {
                    // 若Socket存在但实际已断开,强制清理
                    // 若Socket存在但实际已断开,强制清理
                    if (_clientSocket != null && (_clientSocket.Poll(0, SelectMode.SelectRead) && _clientSocket.Available == 0)) {
                        SafeCloseSocket();
                    }
@@ -72,7 +72,7 @@
                    // 原有逻辑
                    if (_clientSocket != null && _clientSocket.Connected) {
                        //if (ip == _ip && port == _port) {
                            LogHelper.Info($"产线已连接,无需重连,IP:{ip},端口:{port}");
                            LogHelper.Info($"产线已连接,无需重连,IP:{ip},端口:{port}");
                            return false;
                        //}
@@ -82,7 +82,7 @@
                    return Init(ip, port);
                }
                catch (Exception ex) {
                    LogHelper.Error($"产线重连失败,IP:{ip},端口:{port},异常:{ex.Message}", ex);
                    LogHelper.InfoEx(ex);
                    return false;
                }
            }
@@ -134,11 +134,11 @@
                    // 仅在连接成功时启动接收
                    if (_clientSocket.Connected) {
                        LogHelper.Info($"成功连接到服务端:{_ip}:{_port}");
                        LogHelper.Info($"成功连接到服务端:{_ip}:{_port}");
                        _clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceiveCallback, null);
                    }
                    else {
                        LogHelper.Info("连接未成功,关闭Socket");
                        LogHelper.Info("连接未成功,关闭Socket");
                        SafeCloseSocket();
                    }
                }
@@ -147,7 +147,7 @@
                LogHelper.Info("连接过程中Socket被释放");
            }
            catch (Exception ex) {
                LogHelper.Error($"连接失败:{ex.Message}", ex);
                LogHelper.InfoEx(ex);
                SafeCloseSocket();
            }
            finally {
@@ -166,16 +166,16 @@
                    _clientSocket.Close();
                    _clientSocket.Dispose();
                    // 断开后:清除对应IP:Port的接收数据
                    // 断开后:清除对应IP:Port的接收数据
                    string key = $"{_ip}:{_port}";
                    if (_receivedDataQueue.ContainsKey(key)) {
                        _receivedDataQueue.Remove(key);
                        LogHelper.Info($"已清理队列数据,Key:{key}");
                        LogHelper.Info($"已清理队列数据,Key:{key}");
                    }
                }
            }
            catch (Exception ex) {
                LogHelper.Error($"释放Socket资源异常:{ex.Message}", ex);
                LogHelper.InfoEx(ex);
            }
            finally {
                _clientSocket = null;
@@ -203,13 +203,13 @@
                    _clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceiveCallback, null);
                }
                else {
                    // 服务端主动关闭连接,触发清理
                    // 服务端主动关闭连接,触发清理
                    LogHelper.Info("连接已被服务端关闭");
                    SafeCloseSocket();
                }
            }
            catch (Exception ex) {
                LogHelper.Error($"接收数据异常:{ex.Message}", ex);
                LogHelper.InfoEx(ex);
                SafeCloseSocket(); // 异常时主动关闭
            }
        }
@@ -229,19 +229,18 @@
                        //read = result;
                        return false;
                    }
                    LogHelper.Info($"读产线托盘下线数据成功:{BitConverter.ToString(result)}");
                    LogHelper.Info($"读产线托盘下线数据成功:{BitConverter.ToString(result)}");
                    read = result;
                    return true;
                }
                else {
                    //LogHelper.Info($"_clientSocket={_clientSocket} connected={_clientSocket?.Connected}");
                    Link(_ip, _port);
                    LogHelper.Info($"读产线托盘下线数据失败(未连接),准备重连");
                    LogHelper.Info($"读产线托盘下线数据失败 (未连接) ,准备重连");
                    return false;
                }
            }
            catch (Exception ex) {
                //LogHelper.Error($"读产线托盘下线数据失败(发生了异常:{JsonConvert.SerializeObject(ex)}):{ex.Message}", ex);
                LogHelper.InfoEx(ex);
                return false;
                /* 异常处理 */
@@ -256,11 +255,11 @@
        /// <param name="registers">Modbus寄存器数组</param>
        /// <returns>字节数组</returns>
        public static byte[] ConvertRegistersToBytes(ushort[] registers) {
            // 每个寄存器是16位(2字节),所以总字节数是寄存器数量的2倍
            // 每个寄存器是16位(2字节),所以总字节数是寄存器数量的2倍
            byte[] bytes = new byte[registers.Length * 2];
            for (int i = 0; i < registers.Length; i++) {
                // Modbus使用大端序,高位字节在前
                // Modbus使用大端序,高位字节在前
                bytes[i * 2] = (byte)(registers[i] >> 8);     // 高位字节
                bytes[i * 2 + 1] = (byte)(registers[i] & 0xFF); // 低位字节
            }
@@ -276,13 +275,13 @@
        public static string ConvertBytesToString(byte[] bytes) {
            // 查找第一个0x00字节(字符串结束符)的位置
            int length = Array.IndexOf(bytes, (byte)0);
            if (length < 0) length = bytes.Length; // 如果没有结束符,使用全部字节
            if (length < 0) length = bytes.Length; // 如果没有结束符,使用全部字节
            // 根据设备使用的编码转换(常见的有ASCII或UTF-8)
            // 这里使用ASCII编码作为示例,实际应根据设备文档确定编码方式
            // 这里使用ASCII编码作为示例,实际应根据设备文档确定编码方式
            return Encoding.ASCII.GetString(bytes, 0, length);
            // 如果是UTF-8编码,使用下面这行代替上面那行
            // 如果是UTF-8编码,使用下面这行代替上面那行
            // return Encoding.UTF8.GetString(bytes, 0, length);
        }
@@ -294,12 +293,12 @@
                }
                else {
                    Link(_ip, _port);
                    LogHelper.Info($"写电梯入货数据失败(未连接):{Encoding.UTF8.GetString(sends)}");
                    LogHelper.Info($"写电梯入货数据失败 (未连接) :{Encoding.UTF8.GetString(sends)}");
                    return false;
                }
            }
            catch (Exception ex) {
                LogHelper.Error($"写电梯入货数据失败(发送了异常):{ex.Message}", ex);
                LogHelper.InfoEx(ex);
                return false;
            }
        }
@@ -308,17 +307,17 @@
            try {
                if (_clientSocket != null && _clientSocket?.Connected == true) {
                    _receivedDataQueue.TryGetValue($"{_ip}:{_port}", out byte[] result);
                    LogHelper.Info($"读电梯出货数据成功:{BitConverter.ToString(result)}");
                    LogHelper.Info($"读电梯出货数据成功:{BitConverter.ToString(result)}");
                    return result;
                }
                else {
                    Link(_ip, _port);
                    LogHelper.Info($"读电梯出货数据失败(未连接),准备重连");
                    LogHelper.Info($"读电梯出货数据失败 (未连接) ,准备重连");
                    return null;
                }
            }
            catch (Exception ex) {
                LogHelper.Error($"读电梯出货数据失败(发生了异常:{JsonConvert.SerializeObject(ex)}):{ex.Message}", ex);
                LogHelper.InfoEx(ex);
                return null;
                /* 异常处理 */
            }
@@ -326,20 +325,20 @@
        public static string ChekElevator() {
            try {
                var res = "读取电梯数据的model,索引从1开始,满足以下条件才能发任务 \r\n " +
                   "字段,isNormal ,是否正常模式,1:正常模式,第7个Byte右侧第一位Bit \r\n" +
                   "字段,isValid,当前位置是否有效,1:有效,0:不用管,第9个Byte右侧第一位Bit \r\n" +
                   "字段,runMode,电梯运行模式,9=空闲泊停,7=自动运行,第10个Byte\r\n" +
                   "字段,isLock_1_Out,一层出口是否占用,1 = 占用,第14个Byte右侧第一位Bit\r\n" +
                   "字段,isLock_2_Out,二层出口是否占用,1 = 占用,第14个Byte右侧第二位Bit\r\n" +
                   "字段,taskNO,任务号\r\n" +
                   "判断电梯是否符合2楼到1楼搬送条件:isNormal  且 (runMode == 9 或 runMode == 7) 且 !isLock_1_Out \r\n" +
                   "判断电梯是否符合1楼到成品库区条件:isNormal  且 (runMode == 9 或 runMode == 7) 且 isLock_1_Out\r\n";
                var res = "读取电梯数据的model,索引从1开始,满足以下条件才能发任务 \r\n " +
                   "字段,isNormal ,是否正常模式,1:正常模式,第7个Byte右侧第一位Bit \r\n" +
                   "字段,isValid,当前位置是否有效,1:有效,0:不用管,第9个Byte右侧第一位Bit \r\n" +
                   "字段,runMode,电梯运行模式,9=空闲泊停,7=自动运行,第10个Byte\r\n" +
                   "字段,isLock_1_Out,一层出口是否占用,1 = 占用,第14个Byte右侧第一位Bit\r\n" +
                   "字段,isLock_2_Out,二层出口是否占用,1 = 占用,第14个Byte右侧第二位Bit\r\n" +
                   "字段,taskNO,任务号\r\n" +
                   "判断电梯是否符合2楼到1楼搬送条件:isNormal  且 (runMode == 9 或 runMode == 7) 且 !isLock_1_Out \r\n" +
                   "判断电梯是否符合1楼到成品库区条件:isNormal  且 (runMode == 9 或 runMode == 7) 且 isLock_1_Out\r\n";
                var isRead = ReadElevatorOutOk();
                var log = BitConverter.ToString(isRead);
                res += "读取到的电梯byte数组:" + log + "\r\n";
                res += "读取到的电梯byte数组:" + log + "\r\n";
                //if (isRead != null && isRead.Length >= 14) {
                //    var elevatorReadInfo = new ElevatorReadInfo() {
                //        isNormal = (isRead[6] & 1) == 1,
@@ -353,14 +352,14 @@
                //    var res1 = elevatorReadInfo.is2To1Ok();
                //    res += "判断电梯是否符合2楼到1楼搬送条件,如果符合则返回true,结果" + res1 + "\r\n";
                //    res += "判断电梯是否符合2楼到1楼搬送条件,如果符合则返回true,结果" + res1 + "\r\n";
                //    var res2 = elevatorReadInfo.is1ToOk();
                //    res += "判断电梯是否符合1楼到成品库区条件,如果符合则返回true,结果" + res2 + "\r\n";
                //    res += "判断电梯是否符合1楼到成品库区条件,如果符合则返回true,结果" + res2 + "\r\n";
                //}
                //else {
                //    return "读取电梯状态失败,byte数组要求大于等于14个且不为空";
                //    return "读取电梯状态失败,byte数组要求大于等于14个且不为空";
                //}
                return res;
            }
@@ -390,7 +389,7 @@
                return false;
            }
            catch (Exception ex) {
                LogHelper.Error($"判断电梯是否断电(发生了异常:{JsonConvert.SerializeObject(ex)}):{ex.Message}", ex);
                LogHelper.InfoEx(ex);
                return false;
            }
        }
device/TcpServer.cs
@@ -17,14 +17,14 @@
        }
        private void Init(string ip, int port)
        {
            //创建一个新的Socket,这里我们使用最常用的基于TCP的Stream Socket(流式套接字)
            //创建一个新的Socket,这里我们使用最常用的基于TCP的Stream Socket (流式套接字)
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                //将该socket绑定到主机上面的某个端口,端口应该放到配置文件中
                //将该socket绑定到主机上面的某个端口,端口应该放到配置文件中
                socket.Bind(new IPEndPoint(IPAddress.Parse(ip), port));
                Console.WriteLine(port);
                //启动监听,并且设置一个最大的队列长度
                //启动监听,并且设置一个最大的队列长度
                socket.Listen(30);
                //开始接受客户端连接请求
                socket.BeginAccept(new AsyncCallback(ClientAccepted), socket);
@@ -80,7 +80,7 @@
            }
            catch (Exception ex)
            {
                Console.WriteLine($"【接收客户端的消息异常】:" + ex.Message);
                Console.WriteLine($"【接收客户端的消息异常】:" + ex.Message);
            }
            //准备接受下一个客户端请求
            socket.BeginAccept(new AsyncCallback(ClientAccepted), socket);
@@ -128,7 +128,7 @@
                    }
                }
                    //LogHelper.Info($"接收到信息,IP:{remote_ip},MSG:{BitConverter.ToString(buffers[remote_ip])}");
                    //LogHelper.Info($"接收到信息,IP:{remote_ip},MSG:{BitConverter.ToString(buffers[remote_ip])}");
                    if (buffers.Keys.Contains(remote_ip))
                    {
@@ -156,7 +156,7 @@
                            string rfids16 = BitConverter.ToString(rfid);
                            string rfids = Encoding.ASCII.GetString(rfid);
                            //LogHelper.Info($"读卡器校验对应容器号:{rfids},其16进制形式:{rfids16}");
                            //LogHelper.Info($"读卡器校验对应容器号:{rfids},其16进制形式:{rfids16}");
                            //if (ScanCodeHelper.Analysis(remote_ip, rfids))//校验RFID
                            //{
                            //    isCheck[remote_ip] = false;
@@ -170,12 +170,12 @@
                        }
                        else
                        {
                            LogHelper.Info($"不满足读卡器校验规定:IP:{remote_ip},MSG:{message}");
                            LogHelper.Info($"不满足读卡器校验规定:IP:{remote_ip},MSG:{message}");
                        }
                    }
                    catch(Exception ex)
                    {
                        LogHelper.Info($"扫码校验发生了异常:{ex.Message}");
                        LogHelper.Info($"扫码校验发生了异常:{ex.Message}");
                        saoMa[remote_ip] = 4;//扫码异常
                    }
@@ -185,7 +185,7 @@
                    }
                    catch(Exception ex)
                    {
                        LogHelper.Info($"卷帘门校验发生了异常:{ex.Message}");
                        LogHelper.Info($"卷帘门校验发生了异常:{ex.Message}");
                    }
                        //TcpServerSend(remote_ip, System.Text.Encoding.Default.GetBytes(msgSend));
                        Array.Clear(buffers[remote_ip], 0, buffers[remote_ip].Length);//清空当前IP Buffer
@@ -198,12 +198,12 @@
                        }
                    }
  
                //接收下一个消息(因为这是一个递归的调用,所以这样就可以一直接收消息了)
                //接收下一个消息(因为这是一个递归的调用,所以这样就可以一直接收消息了)
                socket.BeginReceive(buffers[remote_ip], 0, buffers[remote_ip].Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
            }
            catch (Exception ex)
            {
                LogHelper.InfoEx(ex);
            }
        }
@@ -222,7 +222,7 @@
                    try
                    {
                        client.Send(msg);
                        LogHelper.Info($"已发送给该AGV地址{ip},{msg}");
                        LogHelper.Info($"已发送给该AGV地址{ip},{msg}");
                        return true;
                    }
                    catch (SocketException ex)
@@ -240,7 +240,7 @@
            }
            else
            {
                LogHelper.Info("未找到该设备,是否已连接?");
                LogHelper.Info("未找到该设备,是否已连接?");
            }
            return false;
dispatch/GZRobot.cs
@@ -38,7 +38,7 @@
        /// <param name="created_user"></param>
        /// <returns></returns>
        public static int CreateOrder(string taskNo, int priority, string param, string ts = "churuku", string created_user = "hanhe") {
            LogHelper.Info($"CreateOrder参数信息:taskNo:{taskNo},priority:{priority},param:{param},ts:{ts},created_user:{created_user}", "API");
            LogHelper.Info($"CreateOrder参数信息:taskNo:{taskNo},priority:{priority},param:{param},ts:{ts},created_user:{created_user}", "API");
            var msg = "";
            var orderId = 0;
            var data = new OrderInfo() { order_name = taskNo, priority = priority, dead_line = DateTime.Now, ts_name = ts, parameters = param, created_user = created_user };
@@ -54,8 +54,8 @@
                    //    orderId = dataResult.data[0].in_order_id;
                    //}
                    // 返回参数中,code目前不再使用,可通过msg字段判断是否成功,如果msg为”success”则表示成功,否则为报错信息或不存在。http code为422时候的报错为系统报错,其中的msg无法全部获取。
                    // SELFNOTE: 直接调用HttpHelper.Post方法,无法获取header,暂时不考虑
                    // 返回参数中,code目前不再使用,可通过msg字段判断是否成功,如果msg为”success”则表示成功,否则为报错信息或不存在。http code为422时候的报错为系统报错,其中的msg无法全部获取。
                    // SELFNOTE: 直接调用HttpHelper.Post方法,无法获取header,暂时不考虑
                    if (dataResult.msg == "success") {
                        orderId = dataResult.data[0].in_order_id;
                    }
@@ -94,10 +94,10 @@
                //    return result;
                //}
                // 返回参数中,code目前不再使用,可通过msg字段判断是否成功,如果msg为”success”则表示成功,否则为报错信息或不存在。http code为422时候的报错为系统报错,其中的msg无法全部获取。
                // SELFNOTE: 直接调用HttpHelper.Post方法,无法获取header,暂时不考虑
                // 返回参数中,code目前不再使用,可通过msg字段判断是否成功,如果msg为”success”则表示成功,否则为报错信息或不存在。http code为422时候的报错为系统报错,其中的msg无法全部获取。
                // SELFNOTE: 直接调用HttpHelper.Post方法,无法获取header,暂时不考虑
                // 尊重文档的版本
                if (dataResult.msg == "success") { // 目前只取消一个任务,成功无需再检查success_list和error_list
                if (dataResult.msg == "success") { // 目前只取消一个任务,成功无需再检查success_list和error_list
                    Console.WriteLine("[guozi-CancelOrder]取消订单成功");
                    result = true;
                    return result;
@@ -133,10 +133,10 @@
                //    return result;
                //}
                // 返回参数中,code目前不再使用,可通过msg字段判断是否成功,如果msg为”success”则表示成功,否则为报错信息或不存在。http code为422时候的报错为系统报错,其中的msg无法全部获取。
                // SELFNOTE: 直接调用HttpHelper.Post方法,无法获取header,暂时不考虑
                // 返回参数中,code目前不再使用,可通过msg字段判断是否成功,如果msg为”success”则表示成功,否则为报错信息或不存在。http code为422时候的报错为系统报错,其中的msg无法全部获取。
                // SELFNOTE: 直接调用HttpHelper.Post方法,无法获取header,暂时不考虑
                // 尊重文档的版本
                if (dataResult.msg == "success") { // 目前只取消一个任务,成功无需再检查success_list和error_list
                if (dataResult.msg == "success") { // 目前只取消一个任务,成功无需再检查success_list和error_list
                    if (dataResult.success_list.Count == requests.Count && dataResult.error_list.Count == 0) {
                        Console.WriteLine("[guozi-CancelOrder]取消订单成功");
                        result = true;
dispatch/HanAo.cs
@@ -102,7 +102,7 @@
            /// </summary>
            public string trkType { get; set; }
            /// <summary>
            /// 1-999(值越大优先级越高)
            /// 1-999 (值越大优先级越高)
            /// </summary>
            public string trkPrty { get; set; } = "1";
            public string frmPos { get; set; }
@@ -115,7 +115,7 @@
            public string groupNo { get; set; } = "";
            public string clientCode { get; set; } = "WMS";
            /// <summary>
            /// 格式:2022-11-11 11:32:08
            /// 格式:2022-11-11 11:32:08
            /// </summary>
            public string reqTime { get; set; } = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
@@ -144,13 +144,13 @@
            public string requestPk { get; set; }
            public string contNo { get; set; }
            /// <summary>
            /// 双方系统共同定义 1-入库 2-出库 3-移库 (后续如有增加再协定)
            /// 双方系统共同定义 1-入库 2-出库 3-移库  (后续如有增加再协定)
            /// </summary>
            public string noticeType { get; set; }
            public string curPos { get; set; }
            public string noticeInfo { get; set; }
            /// <summary>
            /// 0-成功 (入库上架完成/出库下架完成/库内移库完成:移库只上报最终移库上架) 或 其他-异常码(反馈相关结果原因,WMS根据情况处理
            /// 0-成功  (入库上架完成/出库下架完成/库内移库完成:移库只上报最终移库上架)  或 其他-异常码(反馈相关结果原因,WMS根据情况处理
            /// 1-入库有货 2-入远近有货 3-出库无货 4-出远近有货)
            /// </summary>
            public string code { get; set; }
dispatch/HostToAGV.cs
@@ -3,8 +3,8 @@
//     此代码由工具生成。
//     运行时版本:4.0.30319.42000
//
//     对此文件的更改可能会导致不正确的行为,并且如果
//     重新生成代码,这些更改将会丢失。
//     对此文件的更改可能会导致不正确的行为,并且如果
//     重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
dispatch/NDCApi.cs
@@ -10,7 +10,7 @@
namespace HH.WCS.Mobox3.DSZSH.dispatch
{
    /// <summary>
    /// NDC的API接口,用于替代原NDC、NDCHelper和HostToAGV模块
    /// NDC的API接口,用于替代原NDC,NDCHelper和HostToAGV模块
    /// </summary>
    public class NDCApi
    {
@@ -33,15 +33,15 @@
            try
            {
                string jsonInfo = JsonConvert.SerializeObject(model);
                LogHelper.Info($"任务{taskNo}下发,{jsonInfo}", "NDC");
                LogHelper.Info($"NDC任务下发:{taskNo}\n\n{jsonInfo}\n", "NDC");
                var result = httpH.WebPost(NDCApiUrl + "Add", jsonInfo);
                LogHelper.Info($"任务下发结果res={result}", "NDC");
                LogHelper.Info($"NDC任务下发结果:res='{result}'", "NDC");
                agvApiResult = JsonConvert.DeserializeObject<AgvApiResult>(result);
                return agvApiResult;
            }
            catch (Exception e)
            {
                LogHelper.Info($"任务下发失败 res={e.Message}", "NDC");
                LogHelper.Info($"任务下发失败:res='{e.Message}'", "NDC");
                agvApiResult.err_code = -1;
                agvApiResult.err_msg = e.Message;
                return agvApiResult;
@@ -56,7 +56,7 @@
            try
            {
                string jsonInfo = JsonConvert.SerializeObject(model);
                LogHelper.Info($"任务{model.task_no}下发,{jsonInfo}", "NDC");
                LogHelper.Info($"任务{model.task_no}下发,{jsonInfo}", "NDC");
                var result = httpH.WebPost(NDCApiUrl + "Add", jsonInfo);
                LogHelper.Info($"任务下发结果res={result}", "NDC");
@@ -83,7 +83,7 @@
            try
            {
                string jsonInfo = JsonConvert.SerializeObject(model);
                LogHelper.Info($"任务{model.task_no}取消,{jsonInfo}", "NDC");
                LogHelper.Info($"任务{model.task_no}取消,{jsonInfo}", "NDC");
                var result = httpH.WebPost(NDCApiUrl + "Cancel", jsonInfo);
                LogHelper.Info($"任务{model.task_no}取消结果={result}", "NDC");
@@ -107,7 +107,7 @@
            try
            {
                string jsonInfo = JsonConvert.SerializeObject(model);
                LogHelper.Info($"任务{model.task_no}取消,{jsonInfo}", "NDC");
                LogHelper.Info($"任务{model.task_no}取消,{jsonInfo}", "NDC");
                var result = httpH.WebPost(NDCApiUrl + "Cancel", jsonInfo);
                LogHelper.Info($"任务{model.task_no}取消结果={result}", "NDC");
@@ -135,7 +135,7 @@
            try
            {
                string jsonInfo = JsonConvert.SerializeObject(model);
                LogHelper.Info($"任务{model.task_no}优先级更改,{jsonInfo}", "NDC");
                LogHelper.Info($"任务{model.task_no}优先级更改,{jsonInfo}", "NDC");
                var result = httpH.WebPost(NDCApiUrl + "ChangePri", jsonInfo);
                LogHelper.Info($"任务{model.task_no}优先级更改结果={result}", "NDC");
@@ -159,7 +159,7 @@
            try
            {
                string jsonInfo = JsonConvert.SerializeObject(model);
                LogHelper.Info($"任务{model.task_no}优先级更改,{jsonInfo}", "NDC");
                LogHelper.Info($"任务{model.task_no}优先级更改,{jsonInfo}", "NDC");
                var result = httpH.WebPost(NDCApiUrl + "ChangePri", jsonInfo);
                LogHelper.Info($"任务{model.task_no}优先级更改结果={result}", "NDC");
@@ -187,7 +187,7 @@
            try
            {
                string jsonInfo = JsonConvert.SerializeObject(model);
                LogHelper.Info($"任务{model.task_no}参数更改,{jsonInfo}", "NDC");
                LogHelper.Info($"任务{model.task_no}参数更改,{jsonInfo}", "NDC");
                var result = httpH.WebPost(NDCApiUrl + "ChangeParam", jsonInfo);
                LogHelper.Info($"任务{model.task_no}参数更改结果={result}", "NDC");
@@ -211,7 +211,7 @@
            try
            {
                string jsonInfo = JsonConvert.SerializeObject(model);
                LogHelper.Info($"任务{model.task_no}参数更改,{jsonInfo}", "NDC");
                LogHelper.Info($"任务{model.task_no}参数更改,{jsonInfo}", "NDC");
                var result = httpH.WebPost(NDCApiUrl + "ChangeParam", jsonInfo);
                LogHelper.Info($"任务{model.task_no}参数更改结果={result}", "NDC");
@@ -234,16 +234,16 @@
    /// </summary>
    public class AgvApiResult
    {
        public int err_code { set; get; }//异常码:0 - 正常,其它值为异常错误码
        public string err_msg { set; get; }//返回的错误描述,在 err_code <> 0 时返回
        public object result { set; get; }//正确返回的结果内容,在 err_code = 0 且有返回内容时
        public int err_code { set; get; }//异常码:0 - 正常,其它值为异常错误码
        public string err_msg { set; get; }//返回的错误描述,在 err_code <> 0 时返回
        public object result { set; get; }//正确返回的结果内容,在 err_code = 0 且有返回内容时
    }
    public class AddOrderNewModel
    {
        public int ts_no { set; get; }//TS 号,必须有值
        public int ts_no { set; get; }//TS 号,必须有值
        public int pri { set; get; }//优先级
        public string task_no { set; get; }//上游任务编码,如果 no_feedback = 1 时,可以为空
        public string task_no { set; get; }//上游任务编码,如果 no_feedback = 1 时,可以为空
        public List<param> param { set; get; } = new List<param>();//参数列表
    }
@@ -257,14 +257,14 @@
    public class CancelOrderModel
    {
        public string task_no { set; get; }//上游任务编码
        public bool is_force { set; get; } = true;//是否强制取消,1 – 强制
        public bool is_force { set; get; } = true;//是否强制取消,1 – 强制
    }
    public class ChangeParamModel
    {
        public string task_no { set; get; }//上游任务编码
        public int param_no { set; get; }//参数号
        public string param { set; get; }//参数内容,多个参数以英文分号(;)分隔
        public string param { set; get; }//参数内容,多个参数以英文分号(;)分隔
    }
    public class ChangePriModel
models/BaseModel.cs
@@ -4,12 +4,12 @@
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 【框架】模板抽象类:基本表数据模型
    /// 【框架】模板抽象类:基本表数据模型
    /// </summary>
    public abstract class BaseModel {
        /// <summary>
        /// 唯一识别码(底层主键)
        /// 唯一识别码 (底层主键)
        /// </summary>
        [SugarColumn(IsPrimaryKey = true)]
        public string S_ID { get; set; } = Guid.NewGuid().ToString("D");
@@ -35,7 +35,7 @@
        public DateTime T_MODIFY { get; set; } = DateTime.Now;
        /// <summary>
        /// 数据状态:编辑、定版
        /// 数据状态:编辑,定版
        /// </summary>
        public string S_STATE { get; set; } = "编辑";
    }
models/TN_CAR_IN.cs
@@ -13,7 +13,7 @@
        public string S_CNTR_CODE { set; get; }
        /// <summary>
        /// 生产车数,逗号分割
        /// 生产车数,逗号分割
        /// </summary>
        public string S_CAR_CODE { set; get; }
        public string S_B_STATE { set; get; }//状态
models/TN_CG_Detail.cs
@@ -28,12 +28,12 @@
        public string S_CNTR_CODE { get; set; } = string.Empty;
        /// <summary>
        /// 货品状态:0合格 1待检 2不合格 3抽检中;下线即待检
        /// 货品状态:0合格 1待检 2不合格 3抽检中;下线即待检
        /// </summary>
        public string S_ITEM_STATE { get; set; } = "待检";
        /// <summary>
        /// 货品状态:0合格 1待检 2不合格 3正在检验;下线即待检
        /// 货品状态:0合格 1待检 2不合格 3正在检验;下线即待检
        /// </summary>
        public int N_ITEM_STATE { get; set; } = 1;
@@ -74,7 +74,7 @@
        ///// <summary>
        ///// 产线号
        ///// </summary>
        //public int N_PRODUCT_LINE { get; set; } = 0; // NOTE 后续MES可能会提供,先创建
        //public int N_PRODUCT_LINE { get; set; } = 0; // NOTE 后续MES可能会提供,先创建
        // ----------------
models/TN_Container.cs
@@ -14,11 +14,11 @@
        /// </summary>
        public string S_TYPE { get; set; } = string.Empty;
        /// <summary>
        /// 规格(用于记录容器适用的物料编号)
        /// 规格 (用于记录容器适用的物料编号)
        /// </summary>
        public string S_SPEC { get; set; } = string.Empty;
        /// <summary>
        /// 来源(这里记录为创建该容器的主体:Mobox或具体的任务)
        /// 来源 (这里记录为创建该容器的主体:Mobox或具体的任务)
        /// </summary>
        public string S_SOURCE { get; set; } = string.Empty;
models/TN_Location.cs
@@ -27,7 +27,7 @@
        /// <summary>
        /// 国自 AGV 对应的库位名称
        /// </summary>
        public string S_AGV_SITE { get; set; } = "";
        public string S_AGV_SITE { get; set; } = "0";
        /// <summary>
        /// 货位容器容量
@@ -65,22 +65,22 @@
        // END
        /// <summary>
        /// 上锁状态号:0无 1入库锁 2出库锁 3其它锁
        /// 上锁状态号:0无 1入库锁 2出库锁 3其它锁
        /// </summary>
        public int N_LOCK_STATE { get; set; } = 0;
        /// <summary>
        /// 上锁状态名称:0无 1入库锁 2出库锁 3其它锁
        /// 上锁状态名称:0无 1入库锁 2出库锁 3其它锁
        /// </summary>
        public string S_LOCK_STATE { get; set; } = "无";
        /// <summary>
        /// Lock Operator:上锁的操作来源(通常是任务号)
        /// Lock Operator:上锁的操作来源 (通常是任务号)
        /// </summary>
        public string S_LOCK_OP { get; set; } = "";
        /// <summary>
        /// 货位是否启用:Y启用
        /// 货位是否启用:Y启用
        /// </summary>
        public string C_ENABLE { get; set; } = "Y";
@@ -88,7 +88,7 @@
        /// 货位-容器 关系映射
        /// </summary>
        /// <remarks>
        /// 默认是一对多,通常情况是一对一
        /// 默认是一对多,通常情况是一对一
        /// </remarks>
        [Navigate(NavigateType.OneToMany, nameof(TN_Loc_Container.S_LOC_CODE))]
        public List<TN_Loc_Container> LocCntrRels { get; set; }
models/TN_Outbound_Detail.cs
@@ -13,10 +13,10 @@
        public string S_BS_TYPE { get; set; } = string.Empty; // ERP
        public string S_BS_NO { get; set; } = string.Empty; // ERP 单号(来源单号)
        public string S_BS_NO { get; set; } = string.Empty; // ERP 单号 (来源单号)
        /// <summary>
        /// 业务状态:0待执行 1已下发 2执行中 3已完成
        /// 业务状态:0待执行 1已下发 2执行中 3已完成
        /// </summary>
        public int N_B_STATE { get; set; } = 1; // 创建即执行
@@ -33,12 +33,12 @@
        public string S_BATCH_NO { get; set; }
        /// <summary>
        /// 出库货品数量(整数,用于生成任务数)
        /// 出库货品数量 (整数,用于生成任务数)
        /// </summary>
        public int N_COUNT { get; set; }
        /// <summary>
        /// 终点库区(由WMS下发任务时指定)
        /// 终点库区 (由WMS下发任务时指定)
        /// </summary>
        public string S_END_AREA { get; set; }
models/TN_Outbound_Order.cs
@@ -15,10 +15,10 @@
        public string S_BS_TYPE { get; set; } = string.Empty; // ERP
        public string S_BS_NO { get; set; } = string.Empty; // ERP 单号(来源单号)
        public string S_BS_NO { get; set; } = string.Empty; // ERP 单号 (来源单号)
        /// <summary>
        /// 业务状态:0待执行 1已下发 2执行中 3已完成
        /// 业务状态:0待执行 1已下发 2执行中 3已完成
        /// </summary>
        public int N_B_STATE { get; set; } = 1;
@@ -39,12 +39,12 @@
        //public float F_OUT_QTY { get; set; }
        /// <summary>
        /// 终点库区(由WMS下发任务时指定)
        /// 终点库区 (由WMS下发任务时指定)
        /// </summary>
        public string S_END_AREA { get; set; }
        /// <summary>
        /// 是否强制出库:0不强制 1强制
        /// 是否强制出库:0不强制 1强制
        /// </summary>
        public bool B_FORCE_OUT { get; set; }
models/TN_Outbound_Plan.cs
@@ -13,7 +13,7 @@
    [SugarTable("TN_Outbound_Plan")]
    public class TN_Outbound_Plan : BaseModel {
        /// <summary>
        /// 计划单号(唯一标识)
        /// 计划单号 (唯一标识)
        /// </summary>
        public string JHDH { get; set; }
models/TN_Record_Table.cs
@@ -57,7 +57,7 @@
        public int N_QTY { get; set; } = 0;
        /// <summary>
        /// 货位编码(库内货位,入库就是终点,出库就是起点)
        /// 货位编码 (库内货位,入库就是终点,出库就是起点)
        /// </summary>
        public string S_LOC_CODE { get; set; }
models/TN_RelocationList_Detail.cs
@@ -23,7 +23,7 @@
        public string S_BATCH_NO { get; set; }
        public string S_END_AREA { get; set; }
        /// <summary>
        /// 业务状态:0等待执行 1已执行待生成任务 2任务执行中 3任务完成
        /// 业务状态:0等待执行 1已执行待生成任务 2任务执行中 3任务完成
        /// </summary>
        public int N_B_STATE { get; set; } = 1; // 创建即执行
    }
models/TN_Relocation_List.cs
@@ -19,7 +19,7 @@
        //public int N_COUNT { get; set; }
        public string S_END_AREA { get; set; }
        /// <summary>
        /// 业务状态:0等待执行 1已执行待生成任务 2任务执行中 3任务完成
        /// 业务状态:0等待执行 1已执行待生成任务 2任务执行中 3任务完成
        /// </summary>
        public int N_B_STATE { get; set; } = 0; // 创建后需要确认执行
    }
models/TN_SpotCheck_Detail.cs
@@ -23,7 +23,7 @@
        public string S_BATCH_NO { get; set; }
        public string S_END_AREA { get; set; }
        /// <summary>
        /// 业务状态:0等待执行 1已执行待生成任务 2任务执行中 3任务完成
        /// 业务状态:0等待执行 1已执行待生成任务 2任务执行中 3任务完成
        /// </summary>
        public int N_B_STATE { get; set; } = 1; // 创建即执行
    }
models/TN_Spot_Check.cs
@@ -19,7 +19,7 @@
        //public int N_COUNT { get; set; }
        public string S_END_AREA { get; set; }
        /// <summary>
        /// 业务状态:0等待执行 1已执行待生成任务 2任务执行中 3任务完成
        /// 业务状态:0等待执行 1已执行待生成任务 2任务执行中 3任务完成
        /// </summary>
        public int N_B_STATE { get; set; } = 0; // 创建后需要确认执行
    }
models/TN_Task.cs
@@ -55,12 +55,12 @@
        public string S_EQ_NO { get; set; }
        /// <summary>
        /// 任务状态:0等待 1已推送 2执行 3完成 4错误
        /// 任务状态:0等待 1已推送 2执行 3完成 4错误
        /// </summary>
        public string S_B_STATE { get; set; } = "等待";
        /// <summary>
        /// 任务状态:0等待 1已推送 2执行 3完成 4错误
        /// 任务状态:0等待 1已推送 2执行 3完成 4错误
        /// </summary>
        public int N_B_STATE { get; set; }
   
@@ -92,7 +92,7 @@
        public string S_BS_TYPE { get; set; } = string.Empty;
        ///// <summary>
        ///// 是否强制出库:0不强制 1强制
        ///// 是否强制出库:0不强制 1强制
        ///// </summary>
        //public int N_FORCE { get; set; } = 0;
process/DeviceProcess.cs
@@ -9,7 +9,7 @@
namespace HH.WCS.Mobox3.DSZSH.process
{
    /// <summary>
    /// 设备信号处理,主要是tcp信号,我们做server被动接收信号来处理,根据项目定制的
    /// 设备信号处理,主要是tcp信号,我们做server被动接收信号来处理,根据项目定制的
    /// </summary>
    internal class DeviceProcess
    {
process/TaskProcess.cs
@@ -14,32 +14,32 @@
        #region 任务相关
        //--------------------------------------------------任务相关--------------------------------------------------
        /// <summary>
        /// 取货卸货完成,缓存位状态更新
        /// 取货卸货完成,缓存位状态更新
        /// </summary>
        /// <param name="mst"></param>
        /// <param name="load"></param>
        internal static void BufferLocUpdate(TN_Task mst, bool load) {
            //var trayCarryCount = mst.N_CNTR_COUNT > 0 ? mst.N_CNTR_COUNT : 1;
            if (load) {
                Console.WriteLine($"任务{mst.S_CODE} 货位{mst.S_START_LOC}取货完成,起点解绑容器{mst.S_CNTR_CODE}");
                LogHelper.Info($"任务{mst.S_CODE} 货位{mst.S_START_LOC}取货完成,起点解绑容器{mst.S_CNTR_CODE}");
                Console.WriteLine($"任务{mst.S_CODE} 货位{mst.S_START_LOC}取货完成,起点解绑容器{mst.S_CNTR_CODE}");
                LogHelper.Info($"任务{mst.S_CODE} 货位{mst.S_START_LOC}取货完成,起点解绑容器{mst.S_CNTR_CODE}");
                LocationHelper.UnbindLocCntr(mst.S_START_LOC, mst.S_CNTR_CODE.Split(',').ToList());
            }
            else {
                Console.WriteLine($"任务{mst.S_CODE} 货位{mst.S_END_LOC}卸货完成,终点绑定容器{mst.S_CNTR_CODE}");
                LogHelper.Info($"任务{mst.S_CODE} 货位{mst.S_END_LOC}卸货完成,终点绑定容器{mst.S_CNTR_CODE}");
                Console.WriteLine($"任务{mst.S_CODE} 货位{mst.S_END_LOC}卸货完成,终点绑定容器{mst.S_CNTR_CODE}");
                LogHelper.Info($"任务{mst.S_CODE} 货位{mst.S_END_LOC}卸货完成,终点绑定容器{mst.S_CNTR_CODE}");
                LocationHelper.BindingLoc(mst.S_END_LOC, mst.S_CNTR_CODE.Split(',').ToList());
            }
        }
        /// <summary>
        /// 任务取消,缓存位状态更新
        /// 任务取消,缓存位状态更新
        /// </summary>
        /// <param name="mst"></param>
        internal static void BufferLocCancelUpdate(TN_Task mst) {
            //任务取消,取货完成前的,起点的loadingCount和终点unLoadingCount都清除,取货完成的只处理终点
            //任务取消,取货完成前的,起点的loadingCount和终点unLoadingCount都清除,取货完成的只处理终点
            if (WCSHelper.CheckActionRecordExist(mst.S_CODE, 4)) {
                //根据客户现场要求,如果取货完成任务失败人工拉到终点,我们就当卸货完成处理;如果是人工拉走到其它区域,我们就解锁终点,删除托盘。
                //根据客户现场要求,如果取货完成任务失败人工拉到终点,我们就当卸货完成处理;如果是人工拉走到其它区域,我们就解锁终点,删除托盘。
                //终点绑定
                BufferLocUpdate(mst, false);
                LocationHelper.UnLockLoc(mst.S_END_LOC);
@@ -81,11 +81,11 @@
        /// <param name="extData"></param>
        internal static void OperateReq(string no, int state, string forkliftNo, string extData = "") {
            if (state == 1101) {
                //请求取货,
                //请求取货,
            }
            if (state == 1102) {
                //请求卸货,
                //根据终点判断,是cb02的入口,判断内存中状态(要状态时间),允许卸货,通知agv改参数
                //请求卸货,
                //根据终点判断,是cb02的入口,判断内存中状态 (要状态时间) ,允许卸货,通知agv改参数
                var dic = new Dictionary<string, string>();
                //< Req >< Order No = 'TN2302020002' ParamNo = '18' Param1 = '12' /></ Req >
                dic.Add("No", no);
@@ -95,7 +95,7 @@
                //改完参数车子就会自己卸货
            }
            if (state == 1103) {
                //大铁框叉走以后通知,我们要通知输送线
                //大铁框叉走以后通知,我们要通知输送线
            }
        }
@@ -109,7 +109,7 @@
        internal static bool SendTask(TN_Task mst) {
            var result = false;
            switch (mst.N_SCHEDULE_TYPE) {
                case 1: //通过NDC,hosttoagv调度设备
                case 1: //通过NDC,hosttoagv调度设备
                    return SendNDCTask(mst);
                case 5: //通过杭奥调度设备 
                    return SendHanAoTask(mst);
@@ -127,8 +127,8 @@
            if (mst.N_B_STATE == 0) {
                start = LocationHelper.GetAgvSite(mst.S_START_LOC);
                end = LocationHelper.GetAgvSite(mst.S_END_LOC);
                LogHelper.Info($"NDC推送任务:{mst.S_CODE};start='{start}',end='{end}'");
                LogHelper.Info($"NDC推送任务 {mst.S_CODE};" + "start=" + start + "end= " + end);
                var startLoc = LocationHelper.GetLoc(mst.S_START_LOC);
                var endLoc = LocationHelper.GetLoc(mst.S_END_LOC);
                var dic = new List<param>();
@@ -136,20 +136,19 @@
                dic.Add(new param() { name = "From", value = start.ToString() });
                dic.Add(new param() { name = "To", value = end.ToString() });
                dic.Add(new param() { name = "FUNC", value = startLoc.N_LAYER.ToString() });
                dic.Add(new param() { name = "Ctype", value = "0" });
                var res = NDCApi.AddOrderNew(1, 1, mst.S_CODE, dic);//添加新命令
                if (res != null && (res.err_code == 0 || res.err_code == 50009)) {
                    //推送成功,修改任务优先级
                    //推送成功,修改任务优先级
                    mst.N_B_STATE = 1;
                    mst.S_B_STATE = TN_Task.GetStateStr(1);
                    WCSHelper.UpdateStatus(mst);//更新任务状态
                    result = true;
                    LogHelper.Info($"NDC推送任务成功 {mst.S_CODE}start= {mst.S_START_LOC} + end = {mst.S_END_LOC}");
                    LogHelper.Info($"NDC推送任务成功:{mst.S_CODE};start='{mst.S_START_LOC}',end='{mst.S_END_LOC}'");
                }
                else {
                    LogHelper.Info($"NDC推送任务失败 {mst.S_CODE};Res:" + JsonConvert.SerializeObject(res));
                    LogHelper.Info($"NDC推送任务失败:{mst.S_CODE};Res:" + JsonConvert.SerializeObject(res));
                }
            }
            return result;
swagger.js
@@ -48,7 +48,7 @@
            dataType: "json",
            success: function(data) {
                var modelDesc = data.ModelDesc;
                // 在模型展示区域添加描述
                // 在模型展示区域添加描述
                $(".model-box").each(function() {
                    var modelName = $(this).find(".model-title").text();
                    if (modelDesc[modelName]) {
@@ -75,54 +75,54 @@
/* jshint quotmark: float */
window.SwaggerTranslator.learn({
    "Warning: Deprecated": "警告:已过时",
    "Implementation Notes": "实现备注",
    "Response Class": "响应类",
    "Status": "״̬",
    "Parameters": "参数",
    "Parameter": "参数",
    "Value": "ֵ",
    "Description": "描述",
    "Parameter Type": "参数类型",
    "Data Type": "数据类型",
    "Response Messages": "响应消息",
    "HTTP Status Code": "HTTP 状态码",
    "Reason": "原因",
    "Response Model": "响应模型",
    "Request URL": "请求 URL",
    "Response Body": "响应体",
    "Response Code": "响应码",
    "Response Headers": "响应头",
    "Hide Response": "隐藏响应",
    "Headers": "ͷ",
    "Try it out!": "试一下!",
    "Show/Hide": "显示/隐藏",
    "List Operations": "显示操作",
    "Expand Operations": "展开操作",
    "Raw": "ԭʼ",
    "can't parse JSON.  Raw result": "无法解析 JSON。原始结果",
    "Model Schema": "模型架构",
    "Model": "模型",
    "apply": "应用",
    "Username": "用户名",
    "Password": "密码",
    "Terms of service": "服务条款",
    "Created by": "创建者",
    "See more at": "查看更多:",
    "Contact the developer": "联系开发者",
    "api version": "api 版本",
    "Response Content Type": "响应 Content Type",
    "fetching resource": "正在获取资源",
    "fetching resource list": "正在获取资源列表",
    "Explore": "浏览",
    "Show Swagger Petstore Example Apis": "显示 Swagger Petstore 示例 Apis",
    "Can't read from server.  It may not have the appropriate access-control-origin settings.": "无法从服务器读取。可能没有正确设置 access-control-origin。",
    "Please specify the protocol for": "请指定协议:",
    "Can't read swagger JSON from": "无法读取 swagger JSON于",
    "Finished Loading Resource Information. Rendering Swagger UI": "已加载资源信息。正在渲染 Swagger UI",
    "Unable to read api": "无法读取 api",
    "from path": "从路径",
    "server returned": "服务器返回"
    "Warning: Deprecated": "警告:已过时",
    "Implementation Notes": "实现备注",
    "Response Class": "响应类",
    "Status": "状态",
    "Parameters": "参数",
    "Parameter": "参数",
    "Value": "值",
    "Description": "描述",
    "Parameter Type": "参数类型",
    "Data Type": "数据类型",
    "Response Messages": "响应消息",
    "HTTP Status Code": "HTTP 状态码",
    "Reason": "原因",
    "Response Model": "响应模型",
    "Request URL": "请求 URL",
    "Response Body": "响应体",
    "Response Code": "响应码",
    "Response Headers": "响应头",
    "Hide Response": "隐藏响应",
    "Headers": "头",
    "Try it out!": "试一下!",
    "Show/Hide": "显示/隐藏",
    "List Operations": "显示操作",
    "Expand Operations": "展开操作",
    "Raw": "原始",
    "can't parse JSON.  Raw result": "无法解析 JSON。原始结果",
    "Model Schema": "模型架构",
    "Model": "模型",
    "apply": "应用",
    "Username": "用户名",
    "Password": "密码",
    "Terms of service": "服务条款",
    "Created by": "创建者",
    "See more at": "查看更多:",
    "Contact the developer": "联系开发者",
    "api version": "api 版本",
    "Response Content Type": "响应 Content Type",
    "fetching resource": "正在获取资源",
    "fetching resource list": "正在获取资源列表",
    "Explore": "浏览",
    "Show Swagger Petstore Example Apis": "显示 Swagger Petstore 示例 Apis",
    "Can't read from server.  It may not have the appropriate access-control-origin settings.": "无法从服务器读取。可能没有正确设置 access-control-origin。",
    "Please specify the protocol for": "请指定协议:",
    "Can't read swagger JSON from": "无法读取 swagger JSON于",
    "Finished Loading Resource Information. Rendering Swagger UI": "已加载资源信息。正在渲染 Swagger UI",
    "Unable to read api": "无法读取 api",
    "from path": "从路径",
    "server returned": "服务器返回"
});
$(function ()
{
util/HttpHelper.cs
@@ -9,7 +9,7 @@
    public class HttpHelper {
        public string WebPost(string url, string postData, string cotentType = "application/json")
        {
            Console.WriteLine(url);
            LogHelper.Info($"WebPost:访问:{url}", "API");
            WebRequest request = WebRequest.Create(url);
            request.Method = "POST";
            //string postData = JsonConvert.SerializeObject(data); ;
@@ -35,7 +35,7 @@
            }
            catch (Exception e)
            {
                LogHelper.Info($"WebPost res={e.Message}", "API");
                LogHelper.Info($"WebPost:异常:{e.Message}\n\n{e.StackTrace}\n", "API");
                return "";
            }
            finally
util/LogHelper.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Newtonsoft.Json;
@@ -71,16 +72,20 @@
        }
        #region 自定义方法
        public static void InfoEx(Exception ex) {
            Info($"发生了异常:{ex.Message}\n{ex.StackTrace}");
        public static void Warn(string msg, string preLog = "") {
            Info(preLog + $"出现了[警告]:{msg}", "Warn");
        }
        public static void InfoEx(Exception ex, string preLog = "") {
            Info(preLog + $"发生了[异常]:{ex.Message}\n\n{ex.StackTrace}\n", "Exception");
        }
        public static void InfoApi(string apiName, object model) {
            Info($"触发API:{apiName} " + JsonConvert.SerializeObject(model), "API");
            Info($"触发API:{apiName} " + JsonConvert.SerializeObject(model), "API");
        }
        public static void InfoHostToAGV(string taskName, object model) {
            Info($"AGV任务:{taskName}" + JsonConvert.SerializeObject(model), "HosttoagvTask");
            Info($"AGV任务:{taskName}" + JsonConvert.SerializeObject(model), "HosttoagvTask");
        }
        #endregion
    }
@@ -91,7 +96,7 @@
        /// 通过配置文件配置日志
        /// </summary>
        static LogFactory() {
            var loggerNames = new List<string>() { "HosttoagvTask", "HosttoagvCar", "NDC", "杭奥", "PLC", "API", "OPC" };
            var loggerNames = new List<string>() { "HosttoagvTask", "HosttoagvCar", "NDC", "杭奥", "PLC", "API", "OPC", "Warn", "Exception" };
            LogManager.Configuration = DefaultConfig(loggerNames);
        }
util/SqlHelper.cs
@@ -6,7 +6,7 @@
namespace HH.WCS.Mobox3.DSZSH.util {
    //https://www.donet5.com/Home/Doc
    public class SqlHelper<T> where T : class, new() {
        // NOTE:如果用Oracle数据库,需要包Oracle.ManagedDataAccess/21.15.0,环境netframework 4.6.2(太新了4.8有的服务器安装不上去)
        // NOTE:如果用Oracle数据库,需要包Oracle.ManagedDataAccess/21.15.0,环境netframework 4.6.2 (太新了4.8有的服务器安装不上去)
        public SqlSugarClient GetInstance(string url = "") {
            //创建数据库对象
@@ -54,7 +54,7 @@
                });
            };
            //据转换 (ExecuteCommand才会拦截,查询不行)
            //据转换 (ExecuteCommand才会拦截,查询不行)
            //db.Aop.DataExecuting = (value, entity) => {
            //    //var val=entity.EntityColumnInfo
            //    Console.WriteLine(entity.EntityName);
wms/LocationHelper.cs
@@ -11,7 +11,7 @@
namespace HH.WCS.Mobox3.DSZSH.wms {
    /// <summary>
    /// 货位帮助类(包含货位-容器关系的处理)
    /// 货位帮助类 (包含货位-容器关系的处理)
    /// </summary>
    public class LocationHelper
    {
@@ -111,7 +111,7 @@
        }
        /// <summary>
        /// 取货完解锁起点,卸货完解锁终点,可检验锁的来源,也可以不校验
        /// 取货完解锁起点,卸货完解锁终点,可检验锁的来源,也可以不校验
        /// </summary>
        /// <param name="loc"></param>
        /// <returns></returns>
@@ -146,12 +146,12 @@
        public static string UnbindLocCntr(string loc, List<string> cntrs)
        {
            var db = new SqlHelper<object>().GetInstance();
            var logs = $"货位:{loc},容器:{JsonConvert.SerializeObject(cntrs)}";
            var logs = $"货位:{loc},容器:{JsonConvert.SerializeObject(cntrs)}";
            try
            {
                var lcrList = db.Queryable<TN_Loc_Container>().Where(a => cntrs.Contains(a.S_CNTR_CODE) && a.S_LOC_CODE == loc).ToList();
                if (lcrList.Count == 0) {
                    LogHelper.Info($"货位无需解绑容器,在数据库中未找到{JsonConvert.SerializeObject(cntrs)}相关的货位容器关系表信息");
                    LogHelper.Info($"货位无需解绑容器,在数据库中未找到{JsonConvert.SerializeObject(cntrs)}相关的货位容器关系表信息");
                }
                cntrs = lcrList.Select(a => a.S_CNTR_CODE).ToList();
@@ -167,42 +167,42 @@
                    {
                        if (db.Deleteable<TN_Loc_Container>().Where(it => cntrs.Contains(it.S_CNTR_CODE) && it.S_LOC_CODE == loc).ExecuteCommand() > 0)
                        {
                            LogHelper.Info($"删除货位容器关系表成功,{log}");
                            LogHelper.Info($"删除货位容器关系表成功,{log}");
                        }
                        else
                        {
                            tran.RollbackTran();
                            LogHelper.Info($"删除货位容器关系表失败,{log}");
                            LogHelper.Info($"删除货位容器关系表失败,{log}");
                            return "货位解绑容器失败," + logs;
                            return "货位解绑容器失败," + logs;
                        }
                        log = JsonConvert.SerializeObject(location);
                        if (db.Updateable(location).UpdateColumns(it => new { it.N_CURRENT_NUM, it.S_LOCK_STATE, it.N_LOCK_STATE }).ExecuteCommand() > 0) {
                            tran.CommitTran();
                            LogHelper.Info($"更新货位表成功,{log}");
                            LogHelper.Info($"更新货位表成功,{log}");
                        }
                        else {
                            tran.RollbackTran();
                            LogHelper.Info($"更新货位表失败,{log}");
                            LogHelper.Info($"更新货位表失败,{log}");
                            return "货位解绑容器失败," + logs;
                            return "货位解绑容器失败," + logs;
                        }
                    }
                }
                else
                {
                    LogHelper.Info($"在数据库中未找到该货位,无需更新,货位:{loc}");
                    LogHelper.Info($"在数据库中未找到该货位,无需更新,货位:{loc}");
                }
                return "货位解绑容器成功," + logs;
                return "货位解绑容器成功," + logs;
            }
            catch (Exception ex)
            {
                LogHelper.Info($"发生了异常,货位解绑容器失败,{ex.Message}");
                return "货位绑定容器失败," + logs;
                LogHelper.Info($"发生了异常,货位解绑容器失败,{ex.Message}");
                return "货位绑定容器失败," + logs;
            }
        }
@@ -215,7 +215,7 @@
        public static string BindingLoc(string loc, List<string> cntrs)
        {
            var db = new SqlHelper<object>().GetInstance();
            var logs = $"货位:{loc},容器:{JsonConvert.SerializeObject(cntrs)}";
            var logs = $"货位:{loc},容器:{JsonConvert.SerializeObject(cntrs)}";
            try
            {
                // 删除已经绑定过的容器记录
@@ -230,7 +230,7 @@
                    var cntr = db.Queryable<TN_Container>().Where(c => c.S_CODE == item).First();
                    if (cntr == null) {
                        LogHelper.Info($"货位解绑时,容器{item}没有在容器信息表中查到,不记录容器类型");
                        LogHelper.Info($"货位解绑时,容器{item}没有在容器信息表中查到,不记录容器类型");
                        bindLocCntList.Add(new TN_Loc_Container() { S_LOC_CODE = loc, S_CNTR_CODE = item });
                    }
                    else {
@@ -244,10 +244,10 @@
                {
                    if (db.Insertable(bindLocCntList).ExecuteCommand() <= 0) {
                        db.RollbackTran();
                        LogHelper.Info($"插入货位容器关系表失败,{log}");
                        return "货位绑定容器失败," + logs;
                        LogHelper.Info($"插入货位容器关系表失败,{log}");
                        return "货位绑定容器失败," + logs;
                    }
                    LogHelper.Info($"插入货位容器关系表成功,{log}");
                    LogHelper.Info($"插入货位容器关系表成功,{log}");
                    var location = db.Queryable<TN_Location>().First(a => a.S_CODE == loc);
                    if (location != null)
@@ -261,29 +261,29 @@
                        {
                            db.CommitTran();
                            LogHelper.Info($"更新货位表成功,{log}");
                            LogHelper.Info($"更新货位表成功,{log}");
                        }
                        else
                        {
                            db.RollbackTran();
                            LogHelper.Info($"更新货位表失败,{log}");
                            LogHelper.Info($"更新货位表失败,{log}");
                            return "货位绑定容器失败," + logs;
                            return "货位绑定容器失败," + logs;
                        }
                    }
                    else
                    {
                        db.RollbackTran();
                        LogHelper.Info($"未找到该货位{loc},或者已锁定,{log}");
                        LogHelper.Info($"未找到该货位{loc},或者已锁定,{log}");
                    }
                }
                return "货位绑定容器成功," + logs;
                return "货位绑定容器成功," + logs;
            }
            catch (Exception ex)
            {
                LogHelper.Info($"发生了异常,货位绑定容器失败,");
                return "货位绑定容器失败," + ex.Message;
                LogHelper.Info($"发生了异常,货位绑定容器失败,");
                return "货位绑定容器失败," + ex.Message;
            }
        }
    }
wms/WCSHelper.cs
@@ -53,11 +53,96 @@
        }
        /// <summary>
        /// 起点出库锁(强制赋值,不会检查loc!=null,锁状态=无,需要传参前确认)
        /// ![[弃用|理由:不灵活,涉及业务过于具体]]检查容器类型是否正确
        /// </summary>
        /// <param name="cntrCode"></param>
        /// <param name="cntrType"></param>
        /// <param name="errMsg"></param>
        /// <returns></returns>
        private static bool CheckCntrType(string cntrCode, string cntrType, out string errMsg) {
            var db = new SqlHelper<object>().GetInstance();
            errMsg = string.Empty;
            var cntr = db.Queryable<TN_Container>().Where(c => c.S_CODE == cntrType).First();
            if (cntr == null) {
                errMsg = $"容器'{cntrCode}'在[容器表]中不存在,请在前台页面中维护!";
                return false;
            }
            if (cntr.S_TYPE != cntrType) {
                errMsg = $"容器'{cntrCode}'在[容器表]中的类型是'{cntr.S_TYPE},不是'{cntrType}'!";
                return false;
            }
            return true;
        }
        /// <summary>
        /// 根据容器号,查询当前容器关联的[货位/容器/物料]信息(只查询1条)
        /// </summary>
        /// <param name="cntrCode">容器号</param>
        /// <param name="skipCgDetail">是否跳过[物料表]的查询(当容器号来自[物料表]时)</param>
        /// <returns></returns>
        public static LocCntrCg GetLocCntrCg(string cntrCode, bool skipCgDetail = false) {
            var db = new SqlHelper<object>().GetInstance();
            TN_CG_Detail cgDetail = null;
            if (!skipCgDetail) {
                cgDetail = db.Queryable<TN_CG_Detail>().Where(d => d.S_CNTR_CODE == cntrCode).First();
            }
            var locCntrRel = db.Queryable<TN_Loc_Container>().Where(c => c.S_CNTR_CODE == cntrCode).First();
            TN_Location location = null;
            if (locCntrRel != null) {
                location = db.Queryable<TN_Location>().Where(l => l.S_CODE == locCntrRel.S_LOC_CODE).First();
                if (location == null) {
                    LogHelper.Warn($"");
                }
            }
            if (location != null) {
                location.N_CURRENT_NUM = 0;
                location.T_MODIFY = DateTime.Now;
            }
            return new LocCntrCg {
                CgDetail = cgDetail,
                LocCntrRel = locCntrRel,
                Location = location
            };
        }
        /// <summary>
        /// 绑定[货位-容器]信息
        /// </summary>
        /// <param name="startLoc"></param>
        /// <param name="cntrCode"></param>
        /// <returns></returns>
        public static TN_Loc_Container BindLocCntr(ref TN_Location startLoc, string cntrCode) {
            var locCntrRel = new TN_Loc_Container {
                S_LOC_CODE = startLoc.S_CODE,
                S_CNTR_CODE = cntrCode,
            };
            startLoc.N_CURRENT_NUM = 1;
            startLoc.T_MODIFY = DateTime.Now;
            return locCntrRel;
        }
        /// <summary>
        /// 起点出库锁
        /// </summary>
        /// <param name="loc"></param>
        /// <param name="lockSource"></param>
        public static void LockStartLoc(ref TN_Location loc, string lockSource = "") {
            if (loc == null) {
                throw new ArgumentNullException(); // 接受货位loc为空时直接抛异常(通常不会发生)
            }
            if (loc.N_LOCK_STATE != 0 || loc.S_LOCK_STATE != "无") {
                LogHelper.Warn($"起点出库锁:程序正在尝试给当前[锁状态]不是'无'的货位上锁!货位='{loc.S_CODE}',锁状态=({loc.N_LOCK_STATE},{loc.S_LOCK_STATE})");
            }
            loc.N_LOCK_STATE = 2; // 起点出库锁
            loc.S_LOCK_STATE = TN_Location.GetLockStateStr(2); // 起点出库锁
            loc.S_LOCK_OP = lockSource;
@@ -65,17 +150,35 @@
        }
        /// <summary>
        /// 终点入库锁(强制赋值,不会检查loc!=null,锁状态=无,需要传参前确认)
        /// 终点入库锁
        /// </summary>
        /// <param name="loc"></param>
        /// <param name="lockSource"></param>
        public static void LockEndLoc(ref TN_Location loc, string lockSource = "") {
            if (loc == null) {
                throw new ArgumentNullException(); // 接受货位loc为空时直接抛异常(通常不会发生)
            }
            if (loc.N_LOCK_STATE != 0 || loc.S_LOCK_STATE != "无") {
                LogHelper.Warn($"起点出库锁:程序正在尝试给当前[锁状态]不是'无'的货位上锁!货位='{loc.S_CODE}',锁状态=({loc.N_LOCK_STATE},{loc.S_LOCK_STATE})");
            }
            loc.N_LOCK_STATE = 1; // 终点出库锁
            loc.S_LOCK_STATE = TN_Location.GetLockStateStr(1); // 终点出库锁
            loc.S_LOCK_OP = lockSource;
            loc.T_MODIFY = System.DateTime.Now;
        }
        /// <summary>
        /// 创建任务
        /// </summary>
        /// <param name="startLoc">起点货位:至少提供:S_CODE,S_AREA_CODE</param>
        /// <param name="endLoc">终点货位:至少提供:S_CODE,S_AREA_CODE</param>
        /// <param name="cntId">容器号</param>
        /// <param name="type">任务类型(名称)</param>
        /// <param name="pri">优先级</param>
        /// <param name="agvType">AGV类型</param>
        /// <returns></returns>
        public static TN_Task BuildTask(TN_Location startLoc, TN_Location endLoc, string cntId, string type, int pri = 3, int agvType = 1) {
            TN_Task TN_Task = new TN_Task() {
                S_CODE = GenerateTaskNo(),
@@ -86,12 +189,13 @@
                S_TYPE = type,
                N_PRIORITY = pri,
                N_SCHEDULE_TYPE = agvType,
                N_B_STATE = 0, // 任务创建时,默认等待
                N_B_STATE = 0, // 任务创建时,默认等待
                S_CNTR_CODE = cntId,
            };
            return TN_Task;
        }
        internal static bool CheckActionRecordExist(string no, int code) {
            var db = new SqlHelper<TN_Task_Action>().GetInstance();
@@ -122,7 +226,7 @@
        internal static void Fail(TN_Task task) {
            var db = new SqlHelper<TN_Task>().GetInstance();
            if (task != null) {
                //判断有没有取货完成,没有就变成失败。有取货完成默认完成了(跟据项目而定,有些项目人工拉走了也没有放到终点)。
                //判断有没有取货完成,没有就变成失败。有取货完成默认完成了 (跟据项目而定,有些项目人工拉走了也没有放到终点) 。
                task.N_B_STATE = 4;
                task.S_B_STATE = TN_Task.GetStateStr(task.N_B_STATE);
                db.Updateable(task).UpdateColumns(it => new { it.N_B_STATE, it.S_B_STATE }).ExecuteCommand();
@@ -146,4 +250,13 @@
            return db.Queryable<TN_Task>().Where(a => a.N_B_STATE == 0 && (a.S_B_STATE == "等待" || a.S_B_STATE == "待推送")).ToList();
        }
    }
    /// <summary>
    /// [货位/容器/物料]信息
    /// </summary>
    public class LocCntrCg {
        public TN_CG_Detail CgDetail { get; set; } = null;
        public TN_Loc_Container LocCntrRel { get; set; } = null;
        public TN_Location Location { get; set; } = null;
    }
}