kazelee
7 小时以前 2d3ee03961d6cfbde70342e8a97bc7b05d0b0dc3
优化代码, 封装货位锁和创建任务流程, 数据库事务等
1个文件已添加
31个文件已修改
744 ■■■■ 已修改文件
HH.WCS.Mobox3.DSZSH.csproj 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Program.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/AgvController.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/ApiHelper.cs 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/ApiModel.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/DebugController.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/ErpController.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/MoboxController.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/WMSController.cs 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
core/Monitor.cs 281 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
core/WCSCore.cs 75 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/ModbusFactory.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/ModbusHelper.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/OpcUaHelper.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/ProductionLineDevice.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/S7Helper.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/TcpClient.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/TcpClientHelper.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
device/TcpServer.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dispatch/NDC.cs 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/BaseModel.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_Container.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_Loc_Container.cs 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_Location.cs 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/TN_Task.cs 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
util/LogHelper.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
util/Settings.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/DbTranHelper.cs 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/LocationHelper.cs 补丁 | 查看 | 原始文档 | blame | 历史
wms/SYSHelper.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/WCSHelper.cs 106 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/WMSHelper.cs 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HH.WCS.Mobox3.DSZSH.csproj
@@ -271,6 +271,7 @@
    <Compile Include="dispatch\HostToAGV.cs" />
    <Compile Include="util\LogHelper.cs" />
    <Compile Include="util\WebHelper.cs" />
    <Compile Include="wms\DbTranHelper.cs" />
    <Compile Include="wms\LocationHelper.cs" />
    <Compile Include="wms\SYSHelper.cs" />
    <Compile Include="Program.cs" />
Program.cs
@@ -1,16 +1,17 @@
using System;
using System.Collections.Generic;
using System.Threading;
using HH.WCS.Mobox3.DSZSH.device;
using HH.WCS.Mobox3.DSZSH.core;
using HH.WCS.Mobox3.DSZSH.device;
using HH.WCS.Mobox3.DSZSH.util;
using Microsoft.Owin.Hosting;
using Topshelf;
using Task = System.Threading.Tasks.Task;
using Monitor = HH.WCS.Mobox3.DSZSH.core.Monitor;
using System.Net.Sockets;
using Task = System.Threading.Tasks.Task;
namespace HH.WCS.Mobox3.DSZSH {
    internal class Program
api/AgvController.cs
@@ -8,6 +8,7 @@
using System.Web.Http;
using HH.WCS.Mobox3.DSZSH.core;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
api/ApiHelper.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using HH.WCS.Mobox3.DSZSH.core;
using HH.WCS.Mobox3.DSZSH.models;
@@ -8,6 +9,7 @@
using HH.WCS.Mobox3.DSZSH.wms;
using Newtonsoft.Json;
using SqlSugar;
using Swashbuckle.Swagger;
@@ -61,9 +63,7 @@
                }
                // 更新[起点/终点]锁状态,创建任务
                WCSHelper.LockStartLoc(ref startLoc);
                WCSHelper.LockEndLoc(ref endLoc);
                var task = WCSHelper.BuildTask(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                using (var tran = db.Ado.UseTran()) {
                    // 删除/更新旧[货位/容器/物料]信息
@@ -148,9 +148,7 @@
                }
                // 起点终点上锁,创建任务
                WCSHelper.LockStartLoc(ref startLoc);
                WCSHelper.LockEndLoc(ref endLoc);
                var task = WCSHelper.BuildTask(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                using (var tran = db.Ado.UseTran()) {
                    if (old.CgDetail != null && db.Deleteable(old.CgDetail).ExecuteCommand() <= 0) {
@@ -237,9 +235,7 @@
                    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);
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                using (var tran = db.Ado.UseTran()) {
                    if (old.CgDetail != null && db.Deleteable(old.CgDetail).ExecuteCommand() <= 0) {
@@ -329,9 +325,7 @@
                    return NewSimpleResult(5, preLog + $"没有找到合适的终点货位!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.EndAreas)}");
                }
                WCSHelper.LockStartLoc(ref startLoc);
                WCSHelper.LockEndLoc(ref endLoc);
                var task = WCSHelper.BuildTask(startLoc, endLoc, model.CntId, taskInfo.TaskName);
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, model.CntId, taskInfo.TaskName);
                cntr.S_SOURCE = task.S_CODE; // 用任务号作为容器更新的依据
                cntr.T_MODIFY = DateTime.Now;
@@ -396,9 +390,7 @@
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0,所在库区={LogObject(taskInfo.EndAreas)}");
                }
                WCSHelper.LockStartLoc(ref startLoc);
                WCSHelper.LockEndLoc(ref endLoc);
                var task = WCSHelper.BuildTask(startLoc, endLoc, model.CntId, taskInfo.TaskName);
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, model.CntId, taskInfo.TaskName);
                
                using (var tran = db.Ado.UseTran()) {
                    // 更新[起点/终点]锁状态,创建任务
@@ -463,10 +455,7 @@
                cgDetail.T_MODIFY = DateTime.Now;
                var cntId = locCntrRel.S_CNTR_CODE;
                var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskInfo.TaskName);
                WCSHelper.LockStartLoc(ref startLoc);
                WCSHelper.LockEndLoc(ref endLoc);
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, cntId, taskInfo.TaskName);
                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) {
@@ -538,9 +527,7 @@
                cgDetail.N_ITEM_STATE = 2;
                cgDetail.S_ITEM_STATE = "不合格";
                WCSHelper.LockStartLoc(ref startLoc);
                WCSHelper.LockEndLoc(ref endLoc);
                var task = WCSHelper.BuildTask(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                using (var tran = db.Ado.UseTran()) {
                    if (db.Updateable(cgDetail).UpdateColumns(it => new { it.N_ITEM_STATE, it.S_ITEM_STATE }).ExecuteCommand() <= 0) {
@@ -601,9 +588,7 @@
                var cntId = locCntrRel.S_CNTR_CODE;
                WCSHelper.LockStartLoc(ref startLoc);
                WCSHelper.LockEndLoc(ref endLoc);
                var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskInfo.TaskName);
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, cntId, taskInfo.TaskName);
                using (var tran = db.Ado.UseTran()) {
                    //if (db.Insertable(locCntrRel).ExecuteCommand() <= 0) {
api/ApiModel.cs
@@ -4,6 +4,8 @@
using System.Text;
using System.Threading.Tasks;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
namespace HH.WCS.Mobox3.DSZSH.api {
api/DebugController.cs
@@ -16,7 +16,6 @@
using static HH.WCS.Mobox3.DSZSH.api.ApiModel;
using static HH.WCS.Mobox3.DSZSH.api.OtherModel;
using static HH.WCS.Mobox3.DSZSH.Config;
using static HH.WCS.Mobox3.DSZSH.core.Monitor;
namespace HH.WCS.Mobox3.DSZSH.api {
api/ErpController.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Web.Http;
using static HH.WCS.Mobox3.DSZSH.api.OtherModel;
using HH.WCS.Mobox3.DSZSH.util;
namespace HH.WCS.Mobox3.DSZSH.api {
    /// <summary>
api/MoboxController.cs
@@ -1,6 +1,7 @@
using System.Web.Http;
using HH.WCS.Mobox3.DSZSH.models;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
api/WMSController.cs
@@ -1,4 +1,6 @@
using Newtonsoft.Json;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Web.Http;
core/Monitor.cs
@@ -22,8 +22,6 @@
        public static void CheckInbound() {                
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            try {
                // 查产线是否有物料信息
                foreach (var prod in Settings.ProductionLines) {
@@ -36,8 +34,7 @@
                    // TCPClient传递信息的时候,不要用断点阻塞程序
                    // {"item_code":"CG1001","batch_no":"BN1001","cntr_code":"CN2505111"}
                    if (!TcpClientHelper.TryReadProductionLine(out byte[] read)) {
                        info = $"测试{prod.Id}号产线{prod.PlcIp}:{prod.PlcPort}:读取产线信息失败";
                        LogHelper.Info(info);
                        LogHelper.Info($"测试{prod.Id}号产线{prod.PlcIp}:{prod.PlcPort}:读取产线信息失败");
                        continue;
                    }
@@ -145,15 +142,12 @@
                    var cntId = detail.S_CNTR_CODE;
                    var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                    var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, cntId, taskName);
                    task.S_OP_CODE = detail.S_OO_NO;
                    if (string.IsNullOrEmpty(detail.S_BS_NO)) {
                        task.S_BS_NO = detail.S_BS_NO;
                        task.S_BS_TYPE = "ERP";
                    }
                    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) {
@@ -199,8 +193,6 @@
        public static void CheckCheckOrder() {
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            var taskInfo = Settings.GetTaskInfo(ETask.C抽检出库);
            var taskName = taskInfo.TaskName;
            const string preLog = "轮询:抽检:";
@@ -208,7 +200,7 @@
            try {
                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.Debug($"轮询:{taskName}:暂无待执行的{taskName}单");
                    LogHelper.Debug(preLog + $"暂无待执行的{taskName}单");
                    return;
                }
@@ -229,7 +221,7 @@
                        .ToList();
                    if (checkDetailList.Count == 0) {
                        LogHelper.Info($"轮询:{taskName}:仍有任务未执行完成,但当前没有已下发的任务");
                        LogHelper.Info(preLog + $"仍有任务未执行完成,但当前没有已下发的任务");
                        continue;
                    }
@@ -243,7 +235,7 @@
                        .Where((l, c) => c.S_CNTR_CODE == detail.S_CNTR_CODE).First();
                    if (startLoc == null) {
                        LogHelper.Info($"轮询:{taskName}:没有找到合适的起点货位!");
                        LogHelper.Info(preLog + $"没有找到合适的起点货位!");
                        continue;
                    }
@@ -253,18 +245,125 @@
                        .Where(a => a.N_CURRENT_NUM == 0).First();
                    if (endLoc == null) {
                        LogHelper.Info($"轮询:{taskName}:没有找到合适的终点货位!");
                        LogHelper.Info(preLog + $"没有找到合适的终点货位!");
                        continue;
                    }
                    detail.N_B_STATE = 2;
                    var cntId = detail.S_CNTR_CODE;
                    var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                    var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, cntId, taskName);
                    task.S_OP_CODE = detail.S_OO_NO;
                    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(preLog + $"修改{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) {
                            tran.RollbackTran();
                            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) {
                            tran.RollbackTran();
                            LogHelper.Info(preLog + $"更新[终点货位锁状态]失败!终点='{endLoc.S_CODE}',锁状态=>'入库锁'");
                            continue;
                        }
                        if (db.Insertable(task).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            LogHelper.Info(preLog + $"生成任务'{taskName}'失败,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}");
                            continue;
                        }
                        tran.CommitTran();
                        LogHelper.Info(preLog + $"生成任务'{taskName}'成功,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}");
                        continue;
                    }
                }
            }
            catch (Exception ex) {
                LogHelper.InfoEx(ex, preLog);
            }
        }
        public static void CheckShiftOrder() {
            var db = new SqlHelper<object>().GetInstance();
            var taskInfo = Settings.GetTaskInfo(ETask.Y移库);
            var taskName = taskInfo.TaskName;
            const string preLog = "轮询:移库:";
            try {
                var orderList = db.Queryable<TN_Relocation_List>()
                    .Where(c => c.N_B_STATE == 1)
                    .OrderBy(c => c.T_CREATE, SqlSugar.OrderByType.Asc)
                    .ToList();
                if (orderList.Count == 0) {
                    LogHelper.Debug(preLog + $"暂无待执行的{taskName}单");
                    return;
                }
                var detailList = new List<TN_RelocationList_Detail>();
                foreach (var order in orderList) {
                    var doingCount = db.Queryable<TN_RelocationList_Detail>()
                        .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(preLog + $"统计{taskName}单={order.S_NO}任务已下发:{doingCount}/{allCount}");
                    if (doingCount == allCount) {
                        order.N_B_STATE = 2; // 所有任务都已执行
                        db.Updateable(order).UpdateColumns(it => new { it.N_B_STATE }).ExecuteCommand();
                        continue;
                    }
                    var checkDetailList = db.Queryable<TN_RelocationList_Detail>()
                        .Where(a => a.S_OO_NO == order.S_NO && a.N_B_STATE == 1) // 已下发
                        .ToList();
                    if (checkDetailList.Count == 0) {
                        LogHelper.Info(preLog + $"仍有任务未执行完成,但当前没有已下发的任务");
                        continue;
                    }
                    foreach (var checkDetail in checkDetailList) {
                        detailList.Add(checkDetail);
                    }
                }
                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();
                    if (startLoc == null) {
                        LogHelper.Info(preLog + $"没有找到终点货位={detail.S_END_AREA}的终点货位!需要满足:未上锁,当前容器数量=0");
                        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_CURRENT_NUM == 0).First();
                    if (endLoc == null) {
                        LogHelper.Info(preLog + $"没有找到终点货位={detail.S_END_AREA}的终点货位!需要满足:未上锁,当前容器数量=0");
                        continue;
                    }
                    detail.N_B_STATE = 2;
                    var cntId = detail.S_CNTR_CODE;
                    var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, cntId, taskName);
                    task.S_OP_CODE = detail.S_OO_NO;
                    using (var tran = db.Ado.UseTran()) {
                        if (db.Updateable(detail).UpdateColumns(it => it.N_B_STATE).ExecuteCommand() <= 0) {
@@ -287,146 +386,12 @@
                        if (db.Insertable(task).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            info = $"生成任务'{taskName}'失败,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                            LogHelper.Info(info);
                            LogHelper.Info(preLog + $"生成任务'{taskName}'失败,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}");
                            continue;
                        }
                        tran.CommitTran();
                        info = $"生成任务'{taskName}'成功,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                        LogHelper.Info(info);
                        continue;
                    }
                }
            }
            catch (Exception ex) {
                LogHelper.InfoEx(ex, preLog);
            }
        }
        public static void CheckShiftOrder() {
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            var taskInfo = Settings.GetTaskInfo(ETask.Y移库);
            var taskName = taskInfo.TaskName;
            const string preLog = "轮询:移库:";
            try {
                var orderList = db.Queryable<TN_Relocation_List>()
                    .Where(c => c.N_B_STATE == 1)
                    .OrderBy(c => c.T_CREATE, SqlSugar.OrderByType.Asc)
                    .ToList();
                if (orderList.Count == 0) {
                    LogHelper.Debug($"轮询:{taskName}:暂无待执行的{taskName}单");
                    return;
                }
                var detailList = new List<TN_RelocationList_Detail>();
                foreach (var order in orderList) {
                    var doingCount = db.Queryable<TN_RelocationList_Detail>()
                        .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}");
                    if (doingCount == allCount) {
                        order.N_B_STATE = 2; // 所有任务都已执行
                        db.Updateable(order).UpdateColumns(it => new { it.N_B_STATE }).ExecuteCommand();
                        continue;
                    }
                    var checkDetailList = db.Queryable<TN_RelocationList_Detail>()
                        .Where(a => a.S_OO_NO == order.S_NO && a.N_B_STATE == 1) // 已下发
                        .ToList();
                    if (checkDetailList.Count == 0) {
                        LogHelper.Info($"轮询:{taskName}:仍有任务未执行完成,但当前没有已下发的任务");
                        continue;
                    }
                    foreach (var checkDetail in checkDetailList) {
                        detailList.Add(checkDetail);
                    }
                }
                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();
                    if (startLoc == null) {
                        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_CURRENT_NUM == 0).First();
                    if (endLoc == null) {
                        info = $"轮询:{taskName}:没有找到终点货位={detail.S_END_AREA}的终点货位!需要满足:未上锁,当前容器数量=0";
                        LogHelper.Info(info);
                        continue;
                    }
                    detail.N_B_STATE = 2;
                    var cntId = detail.S_CNTR_CODE;
                    var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                    task.S_OP_CODE = detail.S_OO_NO;
                    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}单明细表状态为完成--失败!");
                            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) {
                            tran.RollbackTran();
                            info = $"生成任务'{taskName}'失败:更新起点货位{startLoc.S_CODE}锁状态失败";
                            LogHelper.Info(info);
                            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) {
                            tran.RollbackTran();
                            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}";
                            LogHelper.Info(info);
                            continue;
                        }
                        tran.CommitTran();
                        info = $"生成任务'{taskName}'成功,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                        LogHelper.Info(info);
                        LogHelper.Info(preLog + $"生成任务'{taskName}'成功,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}");
                        continue;
                    }
                }
@@ -438,7 +403,6 @@
        public static void CreateTaskReturnErp(TN_Task task) {
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            var httpH = new HttpHelper();
            try {
@@ -447,8 +411,7 @@
                    .First();
                if (plan == null) {
                    info = $"计划单号{task.S_BS_NO}不存在!";
                    LogHelper.Info(info);
                    LogHelper.Info($"计划单号{task.S_BS_NO}不存在!");
                }
                var cgDetail = db.Queryable<TN_CG_Detail>()
@@ -456,8 +419,7 @@
                    .First();
                if (cgDetail == null) {
                    info = $"物料编码不存在!";
                    LogHelper.Info(info);
                    LogHelper.Info($"计划单号{task.S_BS_NO}不存在!");
                }
                var model = new OtherModel.CreateTaskReturnErpInfo {
@@ -527,8 +489,7 @@
                plan.HWZT = model.hwzt;
                plan.PH = model.ph;
                var success = db.Updateable(plan).UpdateColumns(p => new { p.HW, p.HWZT, p.PH }).ExecuteCommand() > 0;
                info = "更新出库计划单记录表" + (success ? "成功" : "失败");
                LogHelper.Info(info);
                LogHelper.Info("更新出库计划单记录表" + (success ? "成功" : "失败"));
            }
            catch (Exception ex) {
                LogHelper.InfoEx(ex);
core/WCSCore.cs
@@ -113,7 +113,6 @@
        private static void RecordTaskTable(TN_Task task) {
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            try {
                var cgDetail = new TN_CG_Detail(); // 如果没有信息,默认就是空值,可以直接填入,不需要判断
@@ -128,8 +127,7 @@
                        .First();
                    if (cgDetail == null) {
                        info = $"任务{task.S_CODE}完成,记录出入库存在问题:无法在容器货品明细表中找到托盘{task.S_CNTR_CODE}对应的物料";
                        LogHelper.Info(info);
                        LogHelper.Info($"任务{task.S_CODE}完成,记录出入库存在问题:无法在容器货品明细表中找到托盘{task.S_CNTR_CODE}对应的物料");
                        //return;
                        cgDetail = new TN_CG_Detail() ;
                        cgDetail.S_ITEM_CODE = "异常";
@@ -150,8 +148,7 @@
                    .Where(r => r.S_TASK_NO == task.S_CODE).First();
                if (record != null) {
                    //needUpdateRecordTable = true;
                    info = $"出入库记录表中已经存在任务号{task.S_CODE}"; // 防止测试时反复调用134562导致重复记录
                    LogHelper.Info(info);
                    LogHelper.Info($"出入库记录表中已经存在任务号{task.S_CODE}"); // 防止测试时反复调用134562导致重复记录
                    return;
                }
@@ -175,13 +172,11 @@
                // 数据库操作
                if (db.Insertable(record).ExecuteCommand() <= 0) {
                    info = "插入出入库记录表失败:" + JsonConvert.SerializeObject(record);
                    LogHelper.Info(info);
                    LogHelper.Info("插入出入库记录表失败:" + JsonConvert.SerializeObject(record));
                    return;
                }
                info = "插入出入库记录表成功";
                LogHelper.Info(info);
                LogHelper.Info("插入出入库记录表成功");
            }
            catch (Exception ex) {
                LogHelper.InfoEx(ex);
@@ -313,7 +308,6 @@
        public static void PickUpReturnErp(TN_Task task) {
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            var httpH = new HttpHelper();
            
            try {
@@ -322,8 +316,7 @@
                    .First();
                if (plan == null) {
                    info = $"计划单号{task.S_BS_NO}不存在!";
                    LogHelper.Info(info);
                    LogHelper.Info($"计划单号{task.S_BS_NO}不存在!");
                }
                var cgDetail = db.Queryable<TN_CG_Detail>()
@@ -331,8 +324,7 @@
                    .First();
                if (cgDetail == null) {
                    info = $"物料编码不存在!";
                    LogHelper.Info(info);
                    LogHelper.Info($"物料编码不存在!");
                }
                var model = new OtherModel.PickUpReturnErpInfo {
@@ -402,8 +394,7 @@
                plan.SFSL = model.sfsl;
                plan.HWZT = model.hwzt;
                var success = db.Updateable(plan).UpdateColumns(p => new { p.SFJS, p.SFSL, p.HWZT }).ExecuteCommand() > 0;
                info = "更新出库计划单记录表" + (success ? "成功" : "失败");
                LogHelper.Info(info);
                LogHelper.Info("更新出库计划单记录表" + (success ? "成功" : "失败"));
            }
            catch (Exception ex) {
                LogHelper.InfoEx(ex);
@@ -496,7 +487,6 @@
        /// <returns></returns>
        public static Result<bool> CreateInboundTask(string startLocCode, string cntrCode) {
            var db = new SqlHelper<object>().GetInstance();
            var info = "";
            var taskInfo = Settings.GetTaskInfo(ETask.M满托下线入库);
            var taskName = taskInfo.TaskName;
@@ -513,9 +503,7 @@
                .First();
                if (startLoc == null) {
                    info = $"没有找到起点货位'{startLocCode}',或不满足要求:未上锁,当前容器数量=0";
                    LogHelper.Info(info);
                    return new Result<bool>(false, info);
                    return NewResult(false, $"没有找到起点货位'{startLocCode}',或不满足要求:未上锁,当前容器数量=0");
                }
                var locCntrRelOld = db.Queryable<TN_Loc_Container>()
@@ -538,71 +526,46 @@
                    .First();
                if (endLoc == null) {
                    info = $"没有找到合适的【终点货位】,需要满足要求:未上锁,当前容器数量=0";
                    LogHelper.Info(info);
                    return new Result<bool>(false, info);
                    return NewResult(false, $"没有找到合适的【终点货位】,需要满足要求:未上锁,当前容器数量=0");
                }
                var cntId = locCntrRel.S_CNTR_CODE;
                var task = WCSHelper.BuildTask(startLoc, endLoc, cntId, taskName);
                WCSHelper.LockStartLoc(ref startLoc); // 起点出库锁
                WCSHelper.LockEndLoc(ref endLoc); // 终点入库锁
                WCSHelper.LockStartLoc(startLoc); // 起点出库锁
                WCSHelper.LockEndLoc(endLoc); // 终点入库锁
                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();
                            info = $"删除旧货位容器关系表失败:货位编码{locCntrRelOld.S_LOC_CODE},容器编码{locCntrRelOld.S_CNTR_CODE}";
                            LogHelper.Info(info);
                            return new Result<bool>(false, info);
                            return NewResult(false, $"删除旧货位容器关系表失败:货位编码{locCntrRelOld.S_LOC_CODE},容器编码{locCntrRelOld.S_CNTR_CODE}");
                        }
                    }
                    if (db.Insertable(locCntrRel).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        info = $"插入货位容器关系表失败:货位编码{locCntrRel.S_LOC_CODE},容器编码{locCntrRel.S_CNTR_CODE}";
                        LogHelper.Info(info);
                        return new Result<bool>(false, info);
                        return NewResult(false, $"插入货位容器关系表失败:货位编码{locCntrRel.S_LOC_CODE},容器编码{locCntrRel.S_CNTR_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) {
                    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);
                        return new Result<bool>(false, info);
                        return NewResult(false, $"生成任务'{taskName}'失败:更新起点货位{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);
                        return new Result<bool>(false, info);
                        return NewResult(false, $"生成任务'{taskName}'失败:更新终点货位{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);
                        return new Result<bool>(false, info);
                        return NewResult(false, $"生成任务'{taskName}'失败,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}");
                    }
                    tran.CommitTran();
                    info = $"生成任务'{taskName}'成功,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}";
                    LogHelper.Info(info);
                    return new Result<bool>(true, info);
                    return NewResult(true, $"生成任务'{taskName}'成功,任务号={task.S_CODE},容器号={cntId},起点={startLoc.S_CODE},终点={endLoc.S_CODE}");
                }
            }
            catch (Exception ex) {
device/ModbusFactory.cs
@@ -9,6 +9,7 @@
using TcpClient = System.Net.Sockets.TcpClient;
using System.IO;
using HH.WCS.Mobox3.DSZSH.util;
namespace HH.WCS.Mobox3.DSZSH.device {
    // 定义Modbus通讯接口
device/ModbusHelper.cs
@@ -5,6 +5,8 @@
using EasyModbus;
using HH.WCS.Mobox3.DSZSH.util;
namespace HH.WCS.Mobox3.DSZSH.device {
    /// <summary>
    /// modbus tcp 用第三方的包
device/OpcUaHelper.cs
@@ -2,6 +2,7 @@
using Opc.Ua;
using System;
using Opc.Ua.Configuration;
using HH.WCS.Mobox3.DSZSH.util;
namespace HH.WCS.Mobox3.DSZSH.device
device/ProductionLineDevice.cs
@@ -1,5 +1,7 @@
using System;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
device/S7Helper.cs
@@ -1,4 +1,6 @@
using HH.WCS.Mobox3.DSZSH.api;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json.Linq;
using S7.Net;
using S7.Net.Types;
device/TcpClient.cs
@@ -1,5 +1,7 @@
using HH.WCS.Mobox3.DSZSH;
using HH.WCS.Mobox3.DSZSH.device;
using HH.WCS.Mobox3.DSZSH.util;
using System;
using System.Collections.Generic;
using System.Linq;
device/TcpClientHelper.cs
@@ -4,6 +4,8 @@
using System.Net.Sockets;
using System.Text;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
namespace HH.WCS.Mobox3.DSZSH.device {
device/TcpServer.cs
@@ -1,4 +1,6 @@
using HH.WCS.Mobox3.DSZSH.dispatch;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
dispatch/NDC.cs
@@ -1,4 +1,6 @@
using Newtonsoft.Json;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
models/BaseModel.cs
@@ -3,40 +3,13 @@
using SqlSugar;
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");
        /// <summary>
        /// 创建人 ID
        /// </summary>
        public string S_CREATOR_ID { get; set; } = "sa";
        /// <summary>
        /// 创建人名称
        /// </summary>
        public string S_CREATOR_NAME { get; set; } = "超级用户";
        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime T_CREATE { get; set; } = DateTime.Now;
        /// <summary>
        /// 修改时间
        /// </summary>
        public DateTime T_MODIFY { get; set; } = DateTime.Now;
        /// <summary>
        /// 数据状态:编辑,定版
        /// </summary>
        public string S_STATE { get; set; } = "编辑";
        public string S_STATE { get; set; } = "编辑"; // 编辑;定版
    }
}
models/TN_Container.cs
@@ -24,9 +24,13 @@
        public int N_DETAIL_COUNT { get; set; }
        #region 非字段部分
        /// <summary>
        /// <!--默认不使用-->SqlSugar关系映射列表
        /// </summary>
        [SugarColumn(IsIgnore = true)]
        [Navigate(NavigateType.OneToMany, nameof(TN_CG_Detail.S_CNTR_CODE), nameof(S_CODE))]
        public List<TN_CG_Detail> CntrItemRels { get; set; }
        #endregion
    }
}
models/TN_Loc_Container.cs
@@ -23,13 +23,18 @@
        /// </summary>
        public string S_CNTR_TYPE { get; set; }
        // 容器-物料 1:n
        #region 非字段部分
        /// <summary>
        /// <!--默认不使用-->SqlSugar关系映射列表
        /// </summary>
        [Navigate(NavigateType.OneToMany, nameof(TN_CG_Detail.S_CNTR_CODE))]
        public List<TN_CG_Detail> CntrItemRels { get; set; }
        // 容器关系表-容器 1:1
        /// <summary>
        /// <!--默认不使用-->SqlSugar关系映射列表
        /// </summary>
        [Navigate(NavigateType.OneToOne, nameof(S_CNTR_CODE))]
        public TN_Container Container { get; set; }
        #endregion
    }
}
models/TN_Location.cs
@@ -3,55 +3,16 @@
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.models {
    /// <summary>
    /// 【框架】货位表
    /// </summary>
    [SugarTable("TN_Location")]
    public class TN_Location : BaseModel {
        /// <summary>
        /// 货位 ID
        /// </summary>
        public string S_CODE { get; set; }
        /// <summary>
        /// 货位名称
        /// </summary>
        public string S_NAME { get; set; } = "";
        /// <summary>
        /// 货位所在区域 ID
        /// </summary>
        public string S_AREA_CODE { get; set; }
        /// <summary>
        /// 国自 AGV 对应的库位名称
        /// </summary>
        public string S_AGV_SITE { get; set; } = "0";
        /// <summary>
        /// 货位容器容量
        /// </summary>
        public int N_CAPACITY { get; set; } = 1;
        /// <summary>
        /// 货位当前容器数量
        /// </summary>
        public int N_CURRENT_NUM { get; set; } = 0;
        /// <summary>
        /// 行号
        /// </summary>
        public int N_ROW { get; set; } = 0;
        /// <summary>
        /// 列号
        /// </summary>
        public int N_COL { get; set; } = 0;
        /// <summary>
        /// 货位的层数
        /// </summary>
        public int N_LAYER { get; set; } = 1;
        // BEG 初始化表格使用
@@ -79,17 +40,12 @@
        /// </summary>
        public string S_LOCK_OP { get; set; } = "";
        /// <summary>
        /// 货位是否启用:Y启用
        /// </summary>
        public string C_ENABLE { get; set; } = "Y";
        #region 非字段部分
        /// <summary>
        /// 货位-容器 关系映射
        /// <!--默认不使用-->SqlSugar关系映射列表
        /// </summary>
        /// <remarks>
        /// 默认是一对多,通常情况是一对一
        /// </remarks>
        [Navigate(NavigateType.OneToMany, nameof(TN_Loc_Container.S_LOC_CODE))]
        public List<TN_Loc_Container> LocCntrRels { get; set; }
@@ -103,5 +59,6 @@
            }
            return str;
        }
        #endregion
    }
}
models/TN_Task.cs
@@ -96,7 +96,8 @@
        ///// </summary>
        //public int N_FORCE { get; set; } = 0;
        internal static string GetStateStr(int state) {
        #region 非字段部分
        public static string GetStateStr(int state) {
            // 0等待 1已推送 2执行 3完成 4错误
            var status = "";
            switch (state) {
@@ -108,5 +109,6 @@
            }
            return status;
        }
        #endregion
    }
}
util/LogHelper.cs
@@ -9,7 +9,7 @@
using NLog.Config;
using NLog.Targets;
namespace HH.WCS.Mobox3.DSZSH {
namespace HH.WCS.Mobox3.DSZSH.util {
    public class LogHelper
    {
        public static Dictionary<string, ILogger> loggers = new Dictionary<string, ILogger>();
util/Settings.cs
@@ -7,7 +7,7 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace HH.WCS.Mobox3.DSZSH {
namespace HH.WCS.Mobox3.DSZSH.util {
    public class Settings
    {
        public static string WebApiUrl { get; set; }
wms/DbTranHelper.cs
New file
@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HH.WCS.Mobox3.DSZSH.models;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.wms {
    /// <summary>
    /// [ 数据库事务处理 ] 帮助类
    /// </summary>
    public class DbTranHelper {
        /// <summary>
        /// 数据库事务处理 ( 创建任务 )
        /// </summary>
        /// <remarks>要求: obj != null</remarks>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static (bool Success, string Message) DoDbTranTask(DbTranTaskObj obj) {
            var db = new SqlHelper<object>().GetInstance();
            try {
                using (var tran = db.Ado.UseTran()) {
                    // 存在[旧]容器关联信息:删除[旧][容器货品明细][货位容器关系],更新[货位][容器数量]
                    if (obj.Old != null) {
                        if (obj.Old.CgDetail != null && db.Deleteable(obj.Old.CgDetail).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            return (false, $"删除[旧物料信息]失败!!数据:\n\n{JsonConvert.SerializeObject(obj.Old.CgDetail)}\n");
                        }
                        if (obj.Old.LocCntrRel != null && db.Deleteable(obj.Old.LocCntrRel).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            return (false, $"删除[旧货位容器关系]失败!!数据:\n\n{JsonConvert.SerializeObject(obj.Old.LocCntrRel)}\n");
                        }
                        if (obj.Old.Location != null && db.Updateable(obj.Old.Location).UpdateColumns(l => new { l.N_CURRENT_NUM, l.T_MODIFY }).ExecuteCommand() <= 0) {
                            tran.RollbackTran();
                            return (false, $"更新[旧货位][当前容器数量]失败!!货位='{obj.Old.Location.S_CODE}',数量=>{obj.Old.Location.N_CURRENT_NUM}");
                        }
                    }
                    // 更新[容器表](业务需要)
                    if (obj.ContainerToUpdate != null && db.Updateable(obj.ContainerToUpdate).UpdateColumns(c => new { c.S_SPEC, c.S_SOURCE, c.T_MODIFY }).ExecuteCommand() <= 0) {
                        return (false, $"更新[容器表]失败!!数据:\n\n{JsonConvert.SerializeObject(obj.ContainerToUpdate)}\n");
                    }
                    // 更新[容器货品明细](业务需要)
                    if (obj.CgDetailToUpdate != null && db.Updateable(obj.CgDetailToUpdate).UpdateColumns(it => new { it.N_ITEM_STATE, it.S_ITEM_STATE, it.T_MODIFY }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return (false, $"更新[物料明细表]失败!!物料号='{obj.CgDetailToUpdate}',物料状态=>'{obj.CgDetailToUpdate.S_ITEM_STATE}'");
                    }
                    // 存在[新][货位容器绑定关系]:插入[货位容器关系表]
                    if (obj.LocCntrRelToInsert != null && db.Insertable(obj.LocCntrRelToInsert).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return (false, $"插入[容器货位绑定表]失败!!数据:\n\n{JsonConvert.SerializeObject(obj.LocCntrRelToInsert)}\n");
                    }
                    // 更新[起点][终点][锁状态],创建[任务]
                    if (obj.StartLocToUpdate != null && db.Updateable(obj.StartLocToUpdate).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 (false, $"更新[起点货位锁状态]失败!!起点='{obj.StartLocToUpdate.S_CODE}',锁状态=>'{obj.StartLocToUpdate.S_LOCK_STATE}'");
                    }
                    if (obj.EndLocToUpdate != null && db.Updateable(obj.EndLocToUpdate).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return (false, $"更新[终点货位锁状态]失败!!终点='{obj.EndLocToUpdate.S_CODE}',锁状态=>'{obj.EndLocToUpdate.S_LOCK_STATE}'");
                    }
                    if (obj.TaskToInsert != null && db.Insertable(obj.TaskToInsert).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return (false, $"生成任务'{obj.TaskToInsert.S_TYPE}'失败!!任务号='{obj.TaskToInsert.S_CODE}',容器号='{obj.TaskToInsert.S_CNTR_CODE}',起点='{obj.TaskToInsert.S_START_LOC}',终点='{obj.TaskToInsert.S_END_LOC}'");
                    }
                    // 提交数据库更改
                    tran.CommitTran();
                    return (true, $"生成任务'{obj.TaskToInsert.S_TYPE}'成功!!任务号='{obj.TaskToInsert.S_CODE}',容器号='{obj.TaskToInsert.S_CNTR_CODE}',起点='{obj.TaskToInsert.S_START_LOC}',终点='{obj.TaskToInsert.S_END_LOC}'");
                }
            }
            catch (Exception) {
                throw; // 由外部方法捕获处理
            }
        }
    }
    public class DbTranTaskObj {
        public LocCntrCg Old { get; set; } = null;
        public TN_Container ContainerToUpdate { get; set; } = null;
        public TN_CG_Detail CgDetailToUpdate { get; set; } = null;
        public TN_Loc_Container LocCntrRelToInsert { get; set; } = null;
        public TN_Location StartLocToUpdate { get; set; } = null;
        public TN_Location EndLocToUpdate { get; set; } = null;
        public TN_Task TaskToInsert { get; set; } = null;
    }
}
wms/LocationHelper.cs
wms/SYSHelper.cs
@@ -9,6 +9,9 @@
using System.Threading.Tasks;
namespace HH.WCS.Mobox3.DSZSH.wms {
    /// <summary>
    /// 序列号生成帮助类 (名称/命名空间为历史遗留问题)
    /// </summary>
    internal class SYSHelper {
        private static object locker = new object();
        internal static int GetSerialNumber(string snType, string prefix) {
wms/WCSHelper.cs
@@ -53,7 +53,7 @@
        }
        /// <summary>
        /// ![[弃用|理由:不灵活,涉及业务过于具体]]检查容器类型是否正确
        /// <!--弃用|理由:不灵活,涉及业务过于具体-->检查容器类型是否正确
        /// </summary>
        /// <param name="cntrCode"></param>
        /// <param name="cntrType"></param>
@@ -96,7 +96,7 @@
            if (locCntrRel != null) {
                location = db.Queryable<TN_Location>().Where(l => l.S_CODE == locCntrRel.S_LOC_CODE).First();
                if (location == null) {
                    LogHelper.Warn($"");
                    LogHelper.Warn($"获取容器关联信息:[货位容器关系]存在,但[货位]不存在!容器='{cntrCode}',货位='{locCntrRel.S_LOC_CODE}'");
                }
            }
            if (location != null) {
@@ -112,19 +112,39 @@
        }
        /// <summary>
        /// 绑定[货位-容器]信息
        /// [绑定[货位-容器]信息](设置货位数量为1;不会检查loc是否为null)
        /// </summary>
        /// <param name="startLoc"></param>
        /// <param name="loc"></param>
        /// <param name="cntrCode"></param>
        /// <returns></returns>
        public static TN_Loc_Container BindLocCntr(ref TN_Location startLoc, string cntrCode) {
        public static TN_Loc_Container BindLocCntr(ref TN_Location loc, string cntrCode) {
            var locCntrRel = new TN_Loc_Container {
                S_LOC_CODE = startLoc.S_CODE,
                S_LOC_CODE = loc.S_CODE,
                S_CNTR_CODE = cntrCode,
            };
            startLoc.N_CURRENT_NUM = 1;
            startLoc.T_MODIFY = DateTime.Now;
            if (loc.N_CURRENT_NUM != 0) {
                LogHelper.Warn($"绑定货位容器:程序正在尝试给[容器数量]不是0的货位,设置[容器数量]为1!货位='{loc.S_CODE}',容器数量={loc.N_CURRENT_NUM}");
            }
            loc.N_CURRENT_NUM = 1;
            loc.T_MODIFY = DateTime.Now;
            return locCntrRel;
        }
        public static TN_Loc_Container BindLocCntrs(ref TN_Location loc, List<string> cntrCodes) {
            var locCntrRel = new TN_Loc_Container {
                S_LOC_CODE = loc.S_CODE,
                S_CNTR_CODE = string.Join(",", cntrCodes)
            };
            if (loc.N_CURRENT_NUM != 0) {
                LogHelper.Warn($"绑定货位容器:程序正在尝试给[容器数量]不是0的货位,设置[容器数量]为1!货位='{loc.S_CODE}',容器数量={loc.N_CURRENT_NUM}");
            }
            loc.N_CURRENT_NUM = 1;
            loc.T_MODIFY = DateTime.Now;
            return locCntrRel;
        }
@@ -132,70 +152,102 @@
        /// <summary>
        /// 起点出库锁
        /// </summary>
        /// <remarks>要求: loc != null; 锁状态='无';</remarks>
        /// <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为空时直接抛异常(通常不会发生)
            }
        public static void LockStartLoc(TN_Location loc, string lockSource = "") {
            if (loc.N_LOCK_STATE != 0 || loc.S_LOCK_STATE != "无") {
                LogHelper.Warn($"起点出库锁:程序正在尝试给当前[锁状态]不是'无'的货位上锁!货位='{loc.S_CODE}',锁状态=({loc.N_LOCK_STATE},{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;
            loc.T_MODIFY = System.DateTime.Now;
            LogHelper.Info($"起点出库锁:起点货位'{loc.S_CODE}'设置[出库锁]");
        }
        /// <summary>
        /// 终点入库锁
        /// </summary>
        /// <remarks>要求: loc != null; 锁状态='无';</remarks>
        /// <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为空时直接抛异常(通常不会发生)
            }
        public static void LockEndLoc(TN_Location loc, string lockSource = "") {
            if (loc.N_LOCK_STATE != 0 || loc.S_LOCK_STATE != "无") {
                LogHelper.Warn($"起点出库锁:程序正在尝试给当前[锁状态]不是'无'的货位上锁!货位='{loc.S_CODE}',锁状态=({loc.N_LOCK_STATE},{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.N_LOCK_STATE = 1; // 终点入库锁
            loc.S_LOCK_STATE = TN_Location.GetLockStateStr(1); // 终点入库锁
            loc.S_LOCK_OP = lockSource;
            loc.T_MODIFY = System.DateTime.Now;
            LogHelper.Info($"终点入库锁:终点货位='{loc.S_CODE}',来源='{lockSource}'");
        }
        /// <summary>
        /// 创建任务
        /// [创建任务](需确保startLoc/endLoc不为null;不会检查货位S_CODE,S_AREA_CODE属性是否合法)
        /// </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>
        /// <param name="agv">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) {
        public static TN_Task BuildTask(TN_Location startLoc, TN_Location endLoc, string cntId, string type, int pri = 3, int agv = 1) {
            TN_Task TN_Task = new TN_Task() {
                S_CODE = GenerateTaskNo(),
                S_START_AREA = startLoc.S_AREA_CODE,
                S_END_AREA = endLoc.S_AREA_CODE,
                S_START_LOC = startLoc.S_CODE,
                S_END_LOC = endLoc.S_CODE,
                S_CNTR_CODE = cntId,
                S_TYPE = type,
                N_PRIORITY = pri,
                N_SCHEDULE_TYPE = agvType,
                N_SCHEDULE_TYPE = agv,
                N_B_STATE = 0, // 任务创建时,默认等待
                S_CNTR_CODE = cntId,
            };
            LogHelper.Info($"创建任务:任务号='{TN_Task.S_CODE}'");
            return TN_Task;
        }
        /// <summary>
        /// [ 创建任务,并上锁 ] ( 需确保startLoc/endLoc不为null ; 不会检查货位S_CODE,S_AREA_CODE属性是否合法 )
        /// </summary>
        /// <remarks>要求 : (1) startLoc / endLoc ≠ null<br/>(2) startLoc / endLoc 存在 S_CODE , S_AREA_CODE</remarks>
        /// <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="agv">AGV类型</param>
        /// <returns></returns>
        public static TN_Task BuildTaskWithLocLock(TN_Location startLoc, TN_Location endLoc, string cntId, string type, int pri = 3, int agv = 1) {
            TN_Task TN_Task = new TN_Task() {
                S_CODE = GenerateTaskNo(),
                S_START_AREA = startLoc.S_AREA_CODE,
                S_END_AREA = endLoc.S_AREA_CODE,
                S_START_LOC = startLoc.S_CODE,
                S_END_LOC = endLoc.S_CODE,
                S_CNTR_CODE = cntId,
                S_TYPE = type,
                N_PRIORITY = pri,
                N_SCHEDULE_TYPE = agv,
                N_B_STATE = 0, // 任务创建时,默认等待
            };
            LogHelper.Info($"创建任务:任务号='{TN_Task.S_CODE}'");
            LockStartLoc(startLoc, TN_Task.S_CODE);
            LockEndLoc(endLoc, TN_Task.S_CODE);
            return TN_Task;
        }
        internal static bool CheckActionRecordExist(string no, int code) {
            var db = new SqlHelper<TN_Task_Action>().GetInstance();
wms/WMSHelper.cs
@@ -1,9 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using HH.WCS.Mobox3.DSZSH.models;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
using static HH.WCS.Mobox3.DSZSH.api.ApiModel;
namespace HH.WCS.Mobox3.DSZSH.wms {
    public class WMSHelper {