using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; using GalaSoft.MvvmLight; using Microsoft.Extensions.DependencyInjection; using NVelocity.Runtime.Directive; using Serilog; using SlnMesnac.Common; using SlnMesnac.Config; using SlnMesnac.Model.domain; using SlnMesnac.Model.dto; using SlnMesnac.Model.Enum; using SlnMesnac.Repository; using SlnMesnac.Repository.service; using SlnMesnac.Repository.service.Impl; using SlnMesnac.Rfid; using SlnMesnac.Rfid.Enum; using SlnMesnac.Serilog; using SlnMesnac.TouchSocket; using SlnMesnac.WPF.Attribute; using SlnMesnac.WPF.Model; using SqlSugar; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Markup; using System.Windows.Threading; using System.Xml.Serialization; using TouchSocket.Core; using static Microsoft.WindowsAPICodePack.Shell.PropertySystem.SystemProperties.System; using Task = System.Threading.Tasks.Task; namespace SlnMesnac.WPF.ViewModel.IndexPage { [RegisterAsSingletonAttribute] public class ProductionLineViewModel : ViewModelBase { #region 参数定义 private static StringChange _StringChange; private String SerialNo = ""; private SerilogHelper _logger; //private ISqlSugarClient? sqlSugarClient; //容器里面的读写器集合 public List rfidList; private AppConfig appConfig; private MeshttpClient meshttpClient; public TcpServer _TcpServer; private DispatcherTimer _timer; private Dictionary _inventoryTimers = new Dictionary(); private ObservableCollection _rfidHistoryRecords = new ObservableCollection(); private RealReadDataImpl databaseService = RealReadDataImpl.Instance; private System.Threading.Timer ReReadTimer; /// /// 每个设备的写入状态(支持多线并行) /// private class DeviceWriteState { public bool IsVerify; public int WriteTime; public string LastWrite = ""; public string Real_LastRFIDEPC = ""; public CancellationTokenSource? VerifyCts; public CancellationTokenSource? WriteCts; } private Dictionary _deviceStates = new(); private DeviceWriteState GetDeviceState(string deviceId) { if (!_deviceStates.TryGetValue(deviceId, out var state)) { state = new DeviceWriteState(); _deviceStates[deviceId] = state; } return state; } #endregion #region 关联属性 public ObservableCollection RFIDHistoryRecords { get { return _rfidHistoryRecords; } set { _rfidHistoryRecords = value; RaisePropertyChanged(() => RFIDHistoryRecords); } } ChangeTypeViewModel ChangeTypeView; private ObservableCollection _Deviceinfo = new ObservableCollection(); public ObservableCollection Deviceinfo { get { return _Deviceinfo; } set { _Deviceinfo = value; RaisePropertyChanged(() => Deviceinfo); } } /// /// 日期时间 /// private DateTime _currentDateTime; public DateTime CurrentDateTime { get => _currentDateTime; set { if (_currentDateTime != value) { _currentDateTime = value; RaisePropertyChanged(() => CurrentDateTime); } } } /// /// 上一次写入状态 /// private string _lastWriteState; public string LastWriteState { get => _lastWriteState; set { if (_lastWriteState != value) { _lastWriteState = value; RaisePropertyChanged(() => LastWriteState); } } } /// /// 当前状态 空闲 盘点中 写入中 /// private string _CurrentState = "空闲"; public string CurrentState { get => _CurrentState; set { if (_CurrentState != value) { _CurrentState = value; RaisePropertyChanged(() => CurrentState); } } } #endregion #region 构造函数 public ProductionLineViewModel() { ChangeTypeView = App.ServiceProvider.GetService(); // 构造函数里注册 WeakReferenceMessenger.Default.Register(this, RefreshOrderNo); WeakReferenceMessenger.Default.Register(this, "Cancel", StopMessage); _StringChange = App.ServiceProvider.GetService(); _logger = App.ServiceProvider.GetService(); appConfig = App.ServiceProvider.GetService(); //sqlSugarClient = App.ServiceProvider.GetService(); _TcpServer = App.ServiceProvider.GetRequiredService(); rfidList = App.ServiceProvider.GetRequiredService>(); rfidList.ForEach(rfid => { rfid._Action += RecvIdentifyData_Instance; //rfid._RefreshLogMessageAction += RefreshLogMessage; }); LoadDeviceInfo(); StartCheckStatus(); //GetRFIDHistoryRecords(); _currentDateTime = DateTime.Now; _timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) // 每秒更新一次 }; _timer.Tick += (s, e) => { CurrentDateTime = DateTime.Now; }; _timer.Start(); Log.Information("RFID输送带系统启动"); } #endregion #region 核心写入算法 /// /// 接收到连续盘点标签返回 /// /// /// private async void RecvIdentifyData_Instance(string iCombineId, List tagInfos) { try { var state = GetDeviceState(iCombineId); state.WriteCts?.Cancel(); var rfidInfo = rfidList.FirstOrDefault(x => x.deviceid == iCombineId); var deviceInfo = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId); var deviceConfig = appConfig.deviceInfoConfig.FirstOrDefault(x => x.Deviceid == iCombineId); //初次写入 if (!state.IsVerify) { //写入前等待标签稳定 if (!state.IsVerify) { await Task.Delay(deviceInfo.WriteDelaySet ?? 2000); } //验证是否已获取订单信息 if (string.IsNullOrEmpty(deviceInfo.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.SerialNo)) { return; } deviceInfo.ReadTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); //拼接订单号写入标签:OrderNo + 处理后的ProductOrder + 序号 string processedProductOrder = ProcessProductOrder(deviceInfo.ProductOrder); string WriteData = deviceInfo.OrderNo + processedProductOrder + deviceInfo.SerialNo; deviceInfo.CurrentState = "写入中"; //上次读的EPC //LastRFIDEPC = tagInfos[0].EPCstring; //保存最原始的EPC(仅第一次读取时保存,后续重试不覆盖) if (string.IsNullOrEmpty(state.Real_LastRFIDEPC)) { state.Real_LastRFIDEPC = tagInfos[0].EPCstring; } //写入前暂停心跳和连续盘点 StopInventoryTimer(iCombineId); await rfidInfo.Set_HeartBeat(0); await Task.Delay(100); //暂停订单自动刷新,防止写入过程中订单切换 WeakReferenceMessenger.Default.Send(string.Empty, "PauseOrderRefresh"); //处理写入字符串,并写入 //var originBytes = tagInfos[0].EPC.Where(b => b != 0x00).ToArray(); bool writeflag = await rfidInfo.Set_Write(tagInfos[0].EPC, WriteData); state.WriteTime++; if (writeflag) //写入成功 { state.LastWrite = WriteData; state.IsVerify = true; state.WriteTime = 0; //开始二次验证 await rfidInfo.Set_BeginIdentify(); // 启动验证超时检测 state.VerifyCts?.Cancel(); state.VerifyCts = new CancellationTokenSource(); var token = state.VerifyCts.Token; var capturedTagInfos = tagInfos; var timeoutMs = deviceConfig.VerifyTimeoutMilliseconds ?? 20000; _ = Task.Run(async () => { try { await Task.Delay(timeoutMs, token); Log.Error($"二次验证超时未读到标签,标签已离开,记为写入失败"); await DataAdd(iCombineId, capturedTagInfos, false); } catch (TaskCanceledException) { } }, token); //开启定时盘点和心跳 StartInventoryTimer(iCombineId, deviceConfig.InventoryIntervalSeconds.Value); await rfidInfo.Set_HeartBeat(5); return; } else //写入失败 { Log.Error($"第{state.WriteTime + 1}次写入失败,重试中..."); //重试前再读取一次标签 await rfidInfo.Set_BeginIdentify(); //设置下次读取超时 state.WriteCts?.Cancel(); state.WriteCts = new CancellationTokenSource(); var token = state.WriteCts.Token; var capturedTagInfos = tagInfos; var timeoutMs = deviceConfig.WriteTimeoutMilliseconds ?? 10000; _ = Task.Run(async () => { try { await Task.Delay(timeoutMs, token); Log.Error($"写入前读取超时,[{tagInfos[0].EPCstring}] 记为写入失败"); await DataAdd(iCombineId, capturedTagInfos, false); state.WriteTime = 0; } catch (TaskCanceledException) { } }, token); StartInventoryTimer(iCombineId, deviceConfig.InventoryIntervalSeconds.Value); await rfidInfo.Set_HeartBeat(5); await Task.Delay(100); } return; } else { await Task.Run(async () => { await Task.Delay(1000); await rfidInfo!.Set_BeginIdentify(); deviceInfo.CurrentState = "盘点中"; }); } } else //二次验证流程 { var judgeString = Encoding.ASCII.GetString(tagInfos[0].EPC); judgeString = judgeString.Replace("\0", ""); //二次验证如果不通过 重新开始盘点 if (judgeString != state.LastWrite) { // 读到标签但数据不匹配,重置超时(标签仍在天线范围内) state.VerifyCts?.Cancel(); state.VerifyCts = new CancellationTokenSource(); var token = state.VerifyCts.Token; var capturedTagInfos = tagInfos; var timeoutMs = deviceConfig?.VerifyTimeoutMilliseconds ?? 20000; _ = Task.Run(async () => { try { await Task.Delay(timeoutMs, token); Log.Error($"二次验证超时写入标签超时,标签已离开,记为写入失败"); await DataAdd(iCombineId, capturedTagInfos, false); } catch (TaskCanceledException) { } }, token); state.IsVerify = false; //state.Real_LastRFIDEPC = LastRFIDEPC; rfidInfo.Set_BeginIdentify().GetAwaiter().GetResult(); deviceInfo.CurrentState = "盘点中"; return; } else //二次验证通过 { state.VerifyCts?.Cancel(); Log.Information($"验证成功"); //插入成功记录 await DataAdd(iCombineId, tagInfos, true); return; } } } catch (Exception e) { Log.Error($"读结果准备写入时异常:{e.Message}"); var deviceInfo = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId); if (deviceInfo != null) deviceInfo.CurrentState = "空闲"; } } /// /// 插入记录 /// /// /// /// private async Task DataAdd(string iCombineId, List tagInfos, bool isSuccess) { var deviceInfo = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId); var rfidInfo = rfidList.FirstOrDefault(x => x.deviceid == iCombineId); var state = GetDeviceState(iCombineId); var deviceConfig = appConfig.deviceInfoConfig.FirstOrDefault(x => x.Deviceid == iCombineId); state.IsVerify = false; deviceInfo.WriteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); deviceInfo.WriteStatus = isSuccess ? "成功" : "失败"; deviceInfo.LastWriteState = isSuccess ? "成功" : "失败"; deviceInfo.RfidASCII = state.LastWrite; deviceInfo.RfidEPC = state.Real_LastRFIDEPC; deviceInfo.SerialNo = (Convert.ToInt32(deviceInfo.SerialNo) + 1).ToString("D2"); //更新下一个要写入的EPC预览 deviceInfo.NextWriteEPC = deviceInfo.OrderNo + ProcessProductOrder(deviceInfo.ProductOrder) + deviceInfo.SerialNo; //保存写入记录 real_readdata real_Readdata = new real_readdata() { objid = Guid.NewGuid().ToString(), serialno = (Convert.ToInt32(deviceInfo.SerialNo) - 1).ToString("D2"), orderno = deviceInfo.OrderNo, lineno = deviceInfo.LineNo, producttype = deviceInfo.ProductType, productorder = ProcessProductOrder(deviceInfo.ProductOrder), rfidepc = deviceInfo.RfidEPC, rfidascii = state.LastWrite, readtime = Convert.ToDateTime(deviceInfo.ReadTime), writetime = Convert.ToDateTime(deviceInfo.WriteTime), writestatus = deviceInfo.WriteStatus }; //插入到数据库历史记录中 var a = databaseService._helper.Insert(real_Readdata); //插入记录到首页临时数据显示 AddRFIDData(iCombineId, tagInfos); //LastRFIDEPC = ""; state.Real_LastRFIDEPC = ""; state.LastWrite = ""; //写入完成,恢复订单自动刷新 WeakReferenceMessenger.Default.Send(string.Empty, "ResumeOrderRefresh"); await rfidInfo.Set_BeginIdentify(); await Task.Delay(100); StartInventoryTimer(iCombineId, deviceConfig.InventoryIntervalSeconds.Value); await rfidInfo.Set_HeartBeat(5); deviceInfo.CurrentState = "盘点中"; return; } /// /// 处理ProductOrder:去掉前导字母和中间的"-" /// 例:"GS426-0250" → "4260250","GSW426-0250" → "4260250" /// private static string ProcessProductOrder(string productOrder) { if (string.IsNullOrEmpty(productOrder)) return ""; return System.Text.RegularExpressions.Regex.Replace(productOrder, @"^[A-Za-z]+|-", ""); } #endregion #region 数据流相关 /// /// 开始订单初次流程 /// /// /// private void RefreshOrderNo(object recipient, Real_DataInfo real_Data) { var deviceInfo = Deviceinfo.FirstOrDefault(x => x.Deviceid == real_Data.Deviceid); var deviceConfig = appConfig.deviceInfoConfig.FirstOrDefault(x => x.Deviceid == real_Data.Deviceid); //检测OrderNo或ProductOrder是否发生变化,变化则重置计数器 bool orderChanged = deviceInfo.OrderNo != real_Data.OrderNo || deviceInfo.ProductOrder != real_Data.ProductOrder; if (orderChanged) { //查询当前订单+产品订单下的最大序列号 string processedPo = ProcessProductOrder(real_Data.ProductOrder); List real_Readdatas = databaseService._helper.Query(x => x.orderno == real_Data.OrderNo && x.productorder == processedPo); string SNo = ""; if (real_Readdatas.Count > 0) { SNo = real_Readdatas.Max(x => x.serialno); SNo = (Convert.ToInt32(SNo) + 1).ToString("D2"); } else { SNo = "01"; } deviceInfo.SerialNo = SNo; } deviceInfo.OrderNo = real_Data.OrderNo; deviceInfo.ProductType = real_Data.ProductType; deviceInfo.LineNo = real_Data.LineNo; deviceInfo.RfidCount = real_Data.RfidCount; deviceInfo.NextProductNo = real_Data.NextProductNo; deviceInfo.ProductOrder = real_Data.ProductOrder; //更新下一个要写入的EPC预览 deviceInfo.NextWriteEPC = deviceInfo.OrderNo + ProcessProductOrder(deviceInfo.ProductOrder) + deviceInfo.SerialNo; rfidList.FirstOrDefault(x => x.deviceid == real_Data.Deviceid).Set_BeginIdentify().GetAwaiter().GetResult(); var state = GetDeviceState(real_Data.Deviceid); state.IsVerify = false; state.Real_LastRFIDEPC = ""; deviceInfo.CurrentState = "盘点中"; // 启动定时盘点 if (deviceConfig?.InventoryIntervalSeconds > 0) { StartInventoryTimer(real_Data.Deviceid, deviceConfig.InventoryIntervalSeconds.Value); } } /// /// 停止盘点消息 /// private void StopMessage(object o, string e) { var rfid = rfidList.FirstOrDefault(x => x.deviceid == e); StopInventoryTimer(e); var res = rfid.Set_StopIdentify().GetAwaiter().GetResult(); if (res) { MessageBox.Show("停止盘点成功!", "系统提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("停止盘点失败!", "系统提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } var deviceInfo = Deviceinfo.FirstOrDefault(x => x.Deviceid == e); if (deviceInfo != null) deviceInfo.CurrentState = "空闲"; } /// /// 初次加载设备信息 /// private void LoadDeviceInfo() { List DeviceInfos = appConfig.deviceInfoConfig.Where(x => x.Collectid == appConfig.StationCode && x.Deleteflag == 0).ToList(); List real_DataInfos = new List(); foreach (var item in DeviceInfos) { Real_DataInfo real_DataInfo = new Real_DataInfo() { Name = item.Name, Deviceid = item.Deviceid, Connectstr = item.Connectstr, LineName = item.Name, LineNo = item.Addr, OrderNo = "", ProductType = "", WriteCount = "", RfidCount = "", IsOnline = "未连接", RfidASCII = "", RfidEPC = "", ReadTime = "", WriteTime = "", ProductStatus = "", WriteStatus = "", WriteDelaySet = item.WriteDelaySet }; real_DataInfos.Add(real_DataInfo); } App.Current.Dispatcher.Invoke(() => { Deviceinfo.Clear(); foreach (var item in real_DataInfos) { Real_DataInfo real_DataInfo = new Real_DataInfo() { Name = item.Name, Deviceid = item.Deviceid, Connectstr = item.Connectstr, LineName = item.Name, LineNo = item.LineNo, OrderNo = item.OrderNo, ProductType = item.ProductType, WriteCount = item.WriteCount, RfidCount = item.RfidCount, IsOnline = item.IsOnline, RfidASCII = item.RfidASCII, ReadTime = item.ReadTime, WriteTime = item.WriteTime, ProductStatus = item.ProductStatus, WriteStatus = item.WriteStatus, RfidEPC = item.RfidEPC, WriteDelaySet = item.WriteDelaySet }; Deviceinfo.Add(real_DataInfo); } }); } /// /// 添加数据的方法 /// /// /// public void AddRFIDData(string iCombineId, List tagInfos) { Task.Run(() => { App.Current.Dispatcher.Invoke(() => { var deviceInfo = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId); if (deviceInfo == null) return; RFIDHistoryRecords.Insert(0, new RFIDRecord { OrderNumber = deviceInfo.OrderNo, LineNumber = deviceInfo.LineNo, ProductType = deviceInfo.ProductType, OriginalEPC = deviceInfo.RfidEPC, RFIDCode = deviceInfo.RfidASCII, ReadTime = deviceInfo.ReadTime, WriteTime = deviceInfo.ReadTime, WriteStatus = deviceInfo.WriteStatus, DeviceId = iCombineId }); //更新过滤后的历史记录 RefreshFilteredRecords(); }); }); } /// /// 当前选中的设备ID(Tab切换时更新) /// private string? _selectedDeviceId; public string? SelectedDeviceId { get => _selectedDeviceId; set { if (_selectedDeviceId != value) { _selectedDeviceId = value; RaisePropertyChanged(() => SelectedDeviceId); RefreshFilteredRecords(); } } } private ObservableCollection _filteredRFIDHistoryRecords = new(); /// /// 按选中设备过滤后的RFID历史记录 /// public ObservableCollection FilteredRFIDHistoryRecords { get => _filteredRFIDHistoryRecords; set { _filteredRFIDHistoryRecords = value; RaisePropertyChanged(() => FilteredRFIDHistoryRecords); } } private void RefreshFilteredRecords() { var filtered = string.IsNullOrEmpty(_selectedDeviceId) ? RFIDHistoryRecords.ToList() : RFIDHistoryRecords.Where(x => x.DeviceId == _selectedDeviceId).ToList(); FilteredRFIDHistoryRecords.Clear(); foreach (var item in filtered) { FilteredRFIDHistoryRecords.Add(item); } } #endregion #region 设备状态检测 private void StartCheckStatus() { Task.Run(async () => { while (true) { try { #region RFID状态 //RefreshLogMessage("检测设备状态"); var batches = SplitListIntoBatches(rfidList, 10); for (int i = 0; i < batches.Count; i++) { var currentBatch = batches[i]; // 显示当前批次信息(UI线程) Console.WriteLine($"{DateTime.Now}开始检测第{i + 1}批(共{currentBatch.Count}个设备)\r\n"); // 异步检测当前批次的设备状态(不阻塞UI) await DetectDeviceBatchAsync(currentBatch); // 批次之间的间隔(最后一批无需等待) if (i < batches.Count - 1) { Console.WriteLine($"{DateTime.Now}第{i + 1}批检测完成,共{batches.Count}批,等待{1000 / 1000}秒后检测下一批\r\n"); await Task.Delay(10); } } #endregion RFID状态 } catch (Exception ex) { Log.Information($"监听设备状态异常:{ex.Message}"); } await Task.Delay(1000 * 20); } }); } private List> SplitListIntoBatches(List sourceList, int batchSize) { var batches = new List>(); for (int i = 0; i < sourceList.Count; i += batchSize) { // 截取每批的设备(最后一批可能不足10个) var batch = sourceList.Skip(i).Take(batchSize).ToList(); batches.Add(batch); } return batches; } private async Task DetectDeviceBatchAsync(List batch) { // 并行检测当前批次的设备(可选,根据接口性能调整) var detectionTasks = batch.Select(device => DetectSingleDeviceAsync(device)); // 等待当前批次所有设备检测完成 await Task.WhenAll(detectionTasks); } /// /// 重连核心算法 /// /// /// private async Task DetectSingleDeviceAsync(RfidAbsractFactory device) { try { //基于心跳检测的断线重连机制 if (GloalVar.HeartBeatRecoard.TryGetValue(device.deviceid, out var value)) { if (value.AddSeconds(15) < DateTime.Now) { await Reconnect(device); Deviceinfo.FirstOrDefault(x => x.Deviceid == device.deviceid).IsOnline = "已连接"; } } else //第一次的逻辑 { await Reconnect(device); Deviceinfo.FirstOrDefault(x => x.Deviceid == device.deviceid).IsOnline = "已连接"; var res = false; //无限获取心跳返回报文 Log.Information($"准备第一次获取心跳"); res = await device.Set_HeartBeat(5); } } catch (Exception ex) { Log.Information($"更新RFID状态异常:{ex.Message}", ex); } } /// /// 无限重连 /// /// /// 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 定时盘点 /// /// 启动定时盘点 /// private void StartInventoryTimer(string deviceId, int intervalSeconds) { StopInventoryTimer(deviceId); var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(intervalSeconds) }; timer.Tick += (s, e) => OnInventoryTimerTick(deviceId); timer.Start(); //OnInventoryTimerTick(deviceId); _inventoryTimers[deviceId] = timer; Log.Information($"设备 {deviceId} 启动定时盘点,间隔 {intervalSeconds} ms"); } /// /// 停止定时盘点 /// private void StopInventoryTimer(string deviceId) { if (_inventoryTimers.TryGetValue(deviceId, out var timer)) { timer.Stop(); _inventoryTimers.Remove(deviceId); Log.Information($"设备 {deviceId} 停止定时盘点"); } } /// /// 定时盘点回调 /// private async void OnInventoryTimerTick(string deviceId) { try { var rfid = rfidList.FirstOrDefault(x => x.deviceid == deviceId); if (rfid != null) { //Log.Information($"设备 {deviceId} 执行定时盘点"); //await rfid.Set_StopIdentify(); //await Task.Delay(100); await rfid.Set_BeginIdentify(); } } catch (Exception ex) { Log.Error($"设备 {deviceId} 定时盘点异常: {ex.Message}"); } } #endregion } }