diff --git a/SlnMesnac.Config/DeviceInfoConfig.cs b/SlnMesnac.Config/DeviceInfoConfig.cs index 9396fa6..b54ca6e 100644 --- a/SlnMesnac.Config/DeviceInfoConfig.cs +++ b/SlnMesnac.Config/DeviceInfoConfig.cs @@ -69,9 +69,14 @@ namespace SlnMesnac.Config public int? WriteCount { get; set; } /// - /// 盘点间隔时间(秒) + /// 盘点间隔时间(毫秒) /// public int? InventoryIntervalSeconds { get; set; } + + /// + /// 二次验证超时时间(毫秒) + /// + public int? VerifyTimeoutMilliseconds { get; set; } } } \ No newline at end of file diff --git a/SlnMesnac.WPF/ViewModel/IndexPage/ChangeTypeViewModel.cs b/SlnMesnac.WPF/ViewModel/IndexPage/ChangeTypeViewModel.cs index e473e86..2f0388c 100644 --- a/SlnMesnac.WPF/ViewModel/IndexPage/ChangeTypeViewModel.cs +++ b/SlnMesnac.WPF/ViewModel/IndexPage/ChangeTypeViewModel.cs @@ -187,8 +187,8 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage Console.WriteLine("调用MES获取订单接口"); //调用MES接口获取生产订单信息 //MessageBox.Show("MES订单获取成功!", "系统提示", MessageBoxButtons.OK, MessageBoxIcon.Information); - string response = "{\"code\":200,\"msg\":\"success\",\"data\":[{\"OrderNo\":\"SS042\",\"ProductCode\":\"4030502500100006\",\"ProductType\":\"钢丝带-ST-井下阻燃钢丝带\",\"ProductName\":\"输送带-井下阻燃钢丝带-ST\",\"ProductSpec\":\"1400mm;ST/S2500*1;8+8;MT/MT;D7.2;P15;N89\",\"PlanQty\":130.00,\"NextProductNo\":2}]}\r\n"; - //string response = await _httpclient.GetMesOrderInfo(lineno, BeginDate.ToString("yyyy-MM-dd")); + //string response = "{\"code\":200,\"msg\":\"success\",\"data\":[{\"OrderNo\":\"SS042\",\"ProductCode\":\"4030502500100006\",\"ProductType\":\"钢丝带-ST-井下阻燃钢丝带\",\"ProductName\":\"输送带-井下阻燃钢丝带-ST\",\"ProductSpec\":\"1400mm;ST/S2500*1;8+8;MT/MT;D7.2;P15;N89\",\"PlanQty\":130.00,\"NextProductNo\":2}]}\r\n"; + string response = await _httpclient.GetMesOrderInfo(lineno, BeginDate.ToString("yyyy-MM-dd")); Console.WriteLine("接收MES接口返回:" + response); // 反序列化 try diff --git a/SlnMesnac.WPF/ViewModel/IndexPage/ProductionLineViewModel.cs b/SlnMesnac.WPF/ViewModel/IndexPage/ProductionLineViewModel.cs index 16217a8..8f77098 100644 --- a/SlnMesnac.WPF/ViewModel/IndexPage/ProductionLineViewModel.cs +++ b/SlnMesnac.WPF/ViewModel/IndexPage/ProductionLineViewModel.cs @@ -43,6 +43,8 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage [RegisterAsSingletonAttribute] public class ProductionLineViewModel : ViewModelBase { + #region 参数定义 + private static StringChange _StringChange; private String SerialNo = ""; private SerilogHelper _logger; @@ -61,6 +63,11 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage private bool IsVerify = false; private string LastWrite; private string LastRFIDEPC; + private CancellationTokenSource? _verifyCts; + + #endregion + + #region 关联属性 public ObservableCollection RFIDHistoryRecords { @@ -85,7 +92,7 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage RaisePropertyChanged(() => Deviceinfo); } } - + /// /// 日期时间 /// @@ -137,6 +144,10 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage } } + #endregion + + #region 构造函数 + public ProductionLineViewModel() { GetOrderInfoCommand = new RelayCommand(t => RefreshData(t)); @@ -173,6 +184,219 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage Log.Information("RFID输送带系统启动"); } + #endregion + + #region 核心写入算法 + + /// + /// 接收到连续盘点标签返回 + /// + /// + /// + private async void RecvIdentifyData_Instance(string iCombineId, List tagInfos) + { + try + { + //初次写入 + if (!IsVerify) + { + //写入前等待标签稳定 + if (!IsVerify) + { + await Task.Delay(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).WriteDelaySet ?? 2000); + } + + //验证是否已获取订单信息 + if (string.IsNullOrEmpty(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).OrderNo)) + { + MessageBox.Show("请先获取MES订单号!", "系统提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + //查询数据库是否已存在记录 + string epcascii = Encoding.ASCII.GetString(tagInfos[0].EPC); + epcascii = epcascii.Replace("\0", ""); + List real_Readdatas = databaseService._helper.Query(x => x.rfidascii == epcascii); + + //如果不存在则写入 + if (real_Readdatas.Count <= 0) + //if (true) + { + //验证是否已获取序列号 + if (string.IsNullOrEmpty(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).SerialNo)) + { + return; + } + Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).ReadTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + //拼接订单号写入标签 + string WriteData = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).OrderNo + + Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).LineNo + + DateTime.Now.ToString("yy") + + Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).NextProductNo + + Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).SerialNo; + CurrentState = "写入中"; + LastRFIDEPC = tagInfos[0].EPCstring; + //写入 三次重写 + lock (_lockObj) //锁住写入过程,防止写入的时候开始定时盘点 + { + var deviceConfig = appConfig.deviceInfoConfig.FirstOrDefault(x => x.Deviceid == iCombineId); + for (int i = 0; i < deviceConfig.WriteCount; i++) + { + bool writeflag = rfidList.FirstOrDefault(x => x.deviceid == iCombineId).Set_Write(tagInfos[0].EPC, WriteData).GetAwaiter().GetResult(); + if (writeflag) + { + LastWrite = WriteData; + IsVerify = true; + + // 启动验证超时检测 + _verifyCts?.Cancel(); + _verifyCts = new CancellationTokenSource(); + var token = _verifyCts.Token; + var capturedTagInfos = tagInfos; + var timeoutMs = deviceConfig.VerifyTimeoutMilliseconds ?? 20000; + _ = Task.Run(async () => + { + try + { + await Task.Delay(timeoutMs, token); + if (IsVerify) + { + Log.Error($"二次验证超时未读到标签,标签已离开,记为写入失败"); + DataAdd(iCombineId, capturedTagInfos, false); + } + } + catch (TaskCanceledException) { } + }, token); + + rfidList.FirstOrDefault(x => x.deviceid == iCombineId).Set_BeginIdentify().GetAwaiter().GetResult(); + return; + } + else + { + Log.Error($"第{i + 1}次写入失败,重试中..."); + continue; + } + } + Log.Error($"写入失败,[{tagInfos[0].EPCstring}] 记为失败写入"); + //插入失败记录 + DataAdd(iCombineId, tagInfos, false); + return; + } + } + else + { + await Task.Run(async () => + { + await Task.Delay(1000); + await rfidList.FirstOrDefault(x => x.deviceid == iCombineId)!.Set_BeginIdentify(); + CurrentState = "盘点中"; + }); + } + } + else //二次验证流程 + { + var judgeString = Encoding.ASCII.GetString(tagInfos[0].EPC); + judgeString = judgeString.Replace("\0", ""); + //二次验证如果不通过 重新开始盘点 + if (judgeString != LastWrite) + { + // 读到标签但数据不匹配,重置超时(标签仍在天线范围内) + _verifyCts?.Cancel(); + _verifyCts = new CancellationTokenSource(); + var token = _verifyCts.Token; + var capturedTagInfos = tagInfos; + var deviceConfig = appConfig.deviceInfoConfig.FirstOrDefault(x => x.Deviceid == iCombineId); + var timeoutMs = deviceConfig?.VerifyTimeoutMilliseconds ?? 20000; + _ = Task.Run(async () => + { + try + { + await Task.Delay(timeoutMs, token); + if (IsVerify) + { + Log.Error($"二次验证超时写入标签超时,标签已离开,记为写入失败"); + DataAdd(iCombineId, capturedTagInfos, false); + } + } + catch (TaskCanceledException) { } + }, token); + + var tempEPC = LastRFIDEPC; + rfidList.FirstOrDefault(x => x.deviceid == iCombineId).Set_BeginIdentify().GetAwaiter().GetResult(); + LastRFIDEPC = tempEPC; + CurrentState = "盘点中"; + return; + } + else //二次验证通过 + { + _verifyCts?.Cancel(); + Log.Information($"验证成功"); + //插入成功记录 + DataAdd(iCombineId, tagInfos, true); + return; + } + } + } + catch (Exception e) + { + Log.Error($"读结果准备写入时异常:{e.Message}"); + CurrentState = "空闲"; + } + } + + /// + /// 插入记录 + /// + /// + /// + /// + private void DataAdd(string iCombineId, List tagInfos, bool isSuccess) + { + IsVerify = false; + Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).WriteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).WriteStatus = isSuccess ? "成功" : "失败"; + LastWriteState = isSuccess ? "成功" : "失败"; + Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).RfidASCII = LastWrite; + Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).RfidEPC = LastRFIDEPC; + Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).SerialNo = (Convert.ToInt32(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).SerialNo) + 1).ToString("D2"); + //保存写入记录 + real_readdata real_Readdata = new real_readdata() + { + objid = Guid.NewGuid().ToString(), + serialno = (Convert.ToInt32(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).SerialNo) - 1).ToString("D2"), + orderno = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).OrderNo, + lineno = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).LineNo, + producttype = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).ProductType, + rfidepc = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).RfidEPC, + rfidascii = LastWrite, + readtime = Convert.ToDateTime(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).ReadTime), + writetime = Convert.ToDateTime(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).WriteTime), + writestatus = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).WriteStatus + }; + + //插入到数据库历史记录中 + var a = databaseService._helper.Insert(real_Readdata); + + //插入记录到首页临时数据显示 + AddRFIDData(iCombineId, tagInfos); + + LastRFIDEPC = ""; + LastWrite = ""; + + rfidList.FirstOrDefault(x => x.deviceid == iCombineId).Set_BeginIdentify().GetAwaiter().GetResult(); + CurrentState = "盘点中"; + return; + } + + #endregion + + #region 数据流相关 + + /// + /// 测试按钮 + /// + /// + private void RefreshData(object? t) { Random random = new Random(); @@ -196,7 +420,11 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage public RelayCommand GetOrderInfoCommand { get; set; } - + /// + /// 开始订单初次流程 + /// + /// + /// private void RefreshOrderNo(object recipient, Real_DataInfo real_Data) { //查询当前订单产线最大序列号 @@ -239,6 +467,9 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage CurrentState = "空闲"; } + /// + /// 初次加载设备信息 + /// private void LoadDeviceInfo() { List DeviceInfos = appConfig.deviceInfoConfig.Where(x => x.Collectid == appConfig.StationCode && x.Deleteflag == 0).ToList(); @@ -299,194 +530,16 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage Deviceinfo.Add(real_DataInfo); } }); - - } /// - /// 接收到连续盘点标签返回 + /// 添加数据的方法 /// /// /// - private async void RecvIdentifyData_Instance(string iCombineId, List tagInfos) - { - try - { - //二次验证 - if (IsVerify) - { - var judgeString = Encoding.ASCII.GetString(tagInfos[0].EPC); - judgeString = judgeString.Replace("\0", ""); - //二次验证如果不通过 重新开始盘点 - if (judgeString != LastWrite) - { - var tempEPC = LastRFIDEPC; - rfidList.FirstOrDefault(x => x.deviceid == iCombineId).Set_BeginIdentify().GetAwaiter().GetResult(); - LastRFIDEPC = tempEPC; - CurrentState = "盘点中"; - //IsVerify = true; - return; - } - else //二次验证通过 - { - Log.Information($"验证成功"); - //插入成功记录 - DataAdd(iCombineId, tagInfos, true); - return; - } - } - else //正常写入 - { - //写入前等待标签稳定 - if (!IsVerify) - { - await Task.Delay(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).WriteDelaySet ?? 2000); - } - - //验证是否已获取订单信息 - if (string.IsNullOrEmpty(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).OrderNo)) - { - MessageBox.Show("请先获取MES订单号!", "系统提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); - return; - } - - //查询数据库是否已存在记录 - string epcascii = Encoding.ASCII.GetString(tagInfos[0].EPC); - epcascii = epcascii.Replace("\0", ""); - List real_Readdatas = databaseService._helper.Query(x => x.rfidascii == epcascii); - - //如果不存在则写入 - if (real_Readdatas.Count <= 0) - //if (true) - { - //验证是否已获取序列号 - if (string.IsNullOrEmpty(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).SerialNo)) - { - return; - } - Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).ReadTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); - //拼接订单号写入标签 - string WriteData = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).OrderNo - + Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).LineNo - + DateTime.Now.ToString("yy") - + Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).NextProductNo - + Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).SerialNo; - CurrentState = "写入中"; - LastRFIDEPC = tagInfos[0].EPCstring; - //写入 三次重写 - lock (_lockObj) - { - var deviceConfig = appConfig.deviceInfoConfig.FirstOrDefault(x => x.Deviceid == iCombineId); - for (int i = 0; i < deviceConfig.WriteCount; i++) - { - bool writeflag = rfidList.FirstOrDefault(x => x.deviceid == iCombineId).Set_Write(tagInfos[0].EPC, WriteData).GetAwaiter().GetResult(); - if (writeflag) - { - LastWrite = WriteData; - IsVerify = true; - rfidList.FirstOrDefault(x => x.deviceid == iCombineId).Set_BeginIdentify().GetAwaiter().GetResult(); - return; - } - else - { - Log.Error($"第{i + 1}次写入失败,重试中..."); - continue; - } - } - Log.Error($"写入失败,[{tagInfos[0].EPCstring}] 记为失败写入"); - //插入失败记录 - DataAdd(iCombineId, tagInfos, false); - return; - } - } - else - { - await Task.Run(async () => - { - await Task.Delay(1000); - await rfidList.FirstOrDefault(x => x.deviceid == iCombineId)!.Set_BeginIdentify(); - CurrentState = "盘点中"; - }); - } - } - } - catch (Exception e) - { - Log.Error($"读结果准备写入时异常:{e.Message}"); - CurrentState = "空闲"; - } - } - - /// - /// 插入记录 - /// - /// - /// - /// - private void DataAdd(string iCombineId, List tagInfos, bool isSuccess) - { - IsVerify = false; - Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).WriteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); - Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).WriteStatus = isSuccess ? "成功" : "失败"; - LastWriteState = isSuccess ? "成功" : "失败"; - Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).RfidASCII = LastWrite; - Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).RfidEPC = LastRFIDEPC; - Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).SerialNo = (Convert.ToInt32(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).SerialNo) + 1).ToString("D2"); - //保存写入记录 - real_readdata real_Readdata = new real_readdata() - { - objid = Guid.NewGuid().ToString(), - serialno = (Convert.ToInt32(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).SerialNo) - 1).ToString("D2"), - orderno = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).OrderNo, - lineno = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).LineNo, - producttype = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).ProductType, - rfidepc = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).RfidEPC, - rfidascii = LastWrite, - readtime = Convert.ToDateTime(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).ReadTime), - writetime = Convert.ToDateTime(Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).WriteTime), - writestatus = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).WriteStatus - }; - - //插入到数据库历史记录中 - var a = databaseService._helper.Insert(real_Readdata); - - //插入记录到首页临时数据显示 - AddRFIDData(iCombineId, tagInfos); - - LastRFIDEPC = ""; - LastWrite = ""; - - rfidList.FirstOrDefault(x => x.deviceid == iCombineId).Set_BeginIdentify().GetAwaiter().GetResult(); - CurrentState = "盘点中"; - return; - } - - public void RefreshEventArgs(string iCombineId, List tagInfos) - { - try - { - App.Current.Dispatcher.BeginInvoke((Action)(() => - { - Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).RfidEPC = tagInfos[0].EPCstring; - Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).RfidASCII = Encoding.ASCII.GetString(tagInfos[0].EPC); - Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).ReadTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); - })); - } - catch (Exception ex) - { - Log.Information($"异常:{ex.Message}"); - } - } - // 添加数据的方法(核心:添加前/后判断行数) public void AddRFIDData(string iCombineId, List tagInfos) { - //// 1. 判断当前行数是否>10,是则清空 - //if (RFIDHistoryRecords.Count >= 10) - //{ - // RFIDHistoryRecords.Clear(); - //} - - Task.Run(() => + Task.Run(() => { App.Current.Dispatcher.Invoke(() => { @@ -503,8 +556,12 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage }); }); }); - } + + #endregion + + #region 设备状态检测 + private void StartCheckStatus() { Task.Run(async () => @@ -590,6 +647,28 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage } } + /// + /// 无限重连 + /// + /// + /// + private async Task Reconnect(RfidAbsractFactory device) + { + var res = false; + do + { + await Task.Delay(1000); + Deviceinfo.FirstOrDefault(x => x.Deviceid == device.deviceid).IsOnline = "未连接"; + Log.Information($"[{device.deviceid}]:[{device.ip}:{device.port}]连接中..."); + res = await device.ConnectAsync(device.ip, device.port, device.deviceid); + } + while (!res); + } + + #endregion + + #region 定时盘点 + /// /// 启动定时盘点 /// @@ -626,7 +705,7 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage { try { - lock(_lockObj) + lock (_lockObj) { var rfid = rfidList.FirstOrDefault(x => x.deviceid == deviceId); if (rfid != null) @@ -644,23 +723,6 @@ namespace SlnMesnac.WPF.ViewModel.IndexPage } } - /// - /// 无限重连 - /// - /// - /// - private async Task Reconnect(RfidAbsractFactory device) - { - var res = false; - do - { - await Task.Delay(1000); - Deviceinfo.FirstOrDefault(x => x.Deviceid == device.deviceid).IsOnline = "未连接"; - Log.Information($"[{device.deviceid}]:[{device.ip}:{device.port}]连接中..."); - res = await device.ConnectAsync(device.ip, device.port, device.deviceid); - } - while (!res); - } - + #endregion } } diff --git a/SlnMesnac.WPF/appsettings.json b/SlnMesnac.WPF/appsettings.json index 8d2959a..723f600 100644 --- a/SlnMesnac.WPF/appsettings.json +++ b/SlnMesnac.WPF/appsettings.json @@ -74,17 +74,30 @@ "deviceInfoConfig": [ { + //设备ID,唯一标识 "DeviceId": "10001", + //设备名称,显示用 "Name": "Line1", + //连接字符串,格式为IP:Port "ConnectStr": "192.168.0.7:20108", + //固定不变 "Connectmode": "1", + //固定不变 设备类型,RFly_I160表示RFly_I160读写器 "Devicetype": "RFly_I160", + //固定不变 "Collectid": "102", + //关键,线体号 "Addr": "VM006", + //删除标签,0表示不删除,1表示删除(删除后不再显示本组机台,一个花括号内为一组) "Deleteflag": "0", + //初次读到数据后写入数据延迟设置,单位毫秒 "WriteDelaySet": 1000, + //写入失败重试次数(每次写入设置为五秒超时时间) "WriteCount": 3, - "InventoryIntervalSeconds": 5000 + //定时重置盘点间隔时间,单位毫秒 + "InventoryIntervalSeconds": 5000, + //二次验证超时时间,单位毫秒 + "VerifyTimeoutMilliseconds": 20000 }, { "DeviceId": "10002", @@ -97,7 +110,8 @@ "Deleteflag": "1", "WriteDelaySet": 1000, "WriteCount": 3, - "InventoryIntervalSeconds": 5000 + "InventoryIntervalSeconds": 5000, + "VerifyTimeoutMilliseconds": 20000 } ],