You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

751 lines
30 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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<RfidAbsractFactory> rfidList;
private AppConfig appConfig;
private MeshttpClient meshttpClient;
public TcpServer _TcpServer;
private DispatcherTimer _timer;
private Dictionary<string, DispatcherTimer> _inventoryTimers = new Dictionary<string, DispatcherTimer>();
private ObservableCollection<RFIDRecord> _rfidHistoryRecords = new ObservableCollection<RFIDRecord>();
private RealReadDataImpl databaseService = RealReadDataImpl.Instance;
private System.Threading.Timer ReReadTimer;
private bool IsVerify = false;
private int WriteTime = 0;
private string LastWrite;
private string LastRFIDEPC;
private CancellationTokenSource? _verifyCts;
private CancellationTokenSource? _writeCts;
#endregion
#region 关联属性
public ObservableCollection<RFIDRecord> RFIDHistoryRecords
{
get { return _rfidHistoryRecords; }
set
{
_rfidHistoryRecords = value;
RaisePropertyChanged(() => RFIDHistoryRecords);
}
}
ChangeTypeViewModel ChangeTypeView;
private ObservableCollection<Real_DataInfo> _Deviceinfo = new ObservableCollection<Real_DataInfo>();
public ObservableCollection<Real_DataInfo> Deviceinfo
{
get { return _Deviceinfo; }
set
{
_Deviceinfo = value;
RaisePropertyChanged(() => Deviceinfo);
}
}
/// <summary>
/// 日期时间
/// </summary>
private DateTime _currentDateTime;
public DateTime CurrentDateTime
{
get => _currentDateTime;
set
{
if (_currentDateTime != value)
{
_currentDateTime = value;
RaisePropertyChanged(() => CurrentDateTime);
}
}
}
/// <summary>
/// 上一次写入状态
/// </summary>
private string _lastWriteState;
public string LastWriteState
{
get => _lastWriteState;
set
{
if (_lastWriteState != value)
{
_lastWriteState = value;
RaisePropertyChanged(() => LastWriteState);
}
}
}
/// <summary>
/// 当前状态 空闲 盘点中 写入中
/// </summary>
private string _CurrentState = "空闲";
public string CurrentState
{
get => _CurrentState;
set
{
if (_CurrentState != value)
{
_CurrentState = value;
RaisePropertyChanged(() => CurrentState);
}
}
}
#endregion
#region 构造函数
public ProductionLineViewModel()
{
ChangeTypeView = App.ServiceProvider.GetService<ChangeTypeViewModel>();
// 构造函数里注册
WeakReferenceMessenger.Default.Register<Real_DataInfo>(this, RefreshOrderNo);
WeakReferenceMessenger.Default.Register<string, string>(this, "Cancel", StopMessage);
_StringChange = App.ServiceProvider.GetService<StringChange>();
_logger = App.ServiceProvider.GetService<SerilogHelper>();
appConfig = App.ServiceProvider.GetService<AppConfig>();
//sqlSugarClient = App.ServiceProvider.GetService<ISqlSugarClient>();
_TcpServer = App.ServiceProvider.GetRequiredService<TcpServer>();
rfidList = App.ServiceProvider.GetRequiredService<List<RfidAbsractFactory>>();
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 核心写入算法
/// <summary>
/// 接收到连续盘点标签返回
/// </summary>
/// <param name="iCombineId"></param>
/// <param name="tagInfos"></param>
private async void RecvIdentifyData_Instance(string iCombineId, List<TagInfo> tagInfos)
{
try
{
_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 (!IsVerify)
{
//写入前等待标签稳定
if (!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_readdata> 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");
//拼接订单号写入标签
string WriteData = deviceInfo.OrderNo
+ deviceInfo.LineNo
+ DateTime.Now.ToString("yy")
+ deviceInfo.NextProductNo
+ deviceInfo.SerialNo;
CurrentState = "写入中";
LastRFIDEPC = tagInfos[0].EPCstring;
//写入前暂停心跳和连续盘点
StopInventoryTimer(iCombineId);
await rfidInfo.Set_HeartBeat(0);
Task.Delay(100).Wait();
//处理写入字符串,并写入
//var originBytes = tagInfos[0].EPC.Where(b => b != 0x00).ToArray();
bool writeflag = rfidInfo.Set_Write(tagInfos[0].EPC, WriteData).GetAwaiter().GetResult();
WriteTime++;
if (writeflag) //写入成功
{
LastWrite = WriteData;
IsVerify = true;
WriteTime = 0;
//开始二次验证
rfidInfo.Set_BeginIdentify().GetAwaiter().GetResult();
// 启动验证超时检测
_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);
Log.Error($"二次验证超时未读到标签,标签已离开,记为写入失败");
await DataAdd(iCombineId, capturedTagInfos, false);
}
catch (TaskCanceledException) { }
}, token);
//开启定时盘点和心跳
StartInventoryTimer(iCombineId, deviceConfig.InventoryIntervalSeconds.Value);
await rfidInfo.Set_HeartBeat(5);
return;
}
else //写入失败
{
Log.Error($"第{WriteTime + 1}次写入失败,重试中...");
//重试前再读取一次标签
rfidInfo.Set_BeginIdentify().GetAwaiter().GetResult();
//设置下次读取超时
_writeCts?.Cancel();
_writeCts = new CancellationTokenSource();
var token = _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);
WriteTime = 0;
}
catch (TaskCanceledException) { }
}, token);
StartInventoryTimer(iCombineId, deviceConfig.InventoryIntervalSeconds.Value);
await rfidInfo.Set_HeartBeat(5);
Task.Delay(100).Wait();
}
return;
}
else
{
await Task.Run(async () =>
{
await Task.Delay(1000);
await rfidInfo!.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 timeoutMs = deviceConfig?.VerifyTimeoutMilliseconds ?? 20000;
_ = Task.Run(async () =>
{
try
{
await Task.Delay(timeoutMs, token);
Log.Error($"二次验证超时写入标签超时,标签已离开,记为写入失败");
await DataAdd(iCombineId, capturedTagInfos, false);
}
catch (TaskCanceledException) { }
}, token);
IsVerify = false;
var tempEPC = LastRFIDEPC;
rfidInfo.Set_BeginIdentify().GetAwaiter().GetResult();
LastRFIDEPC = tempEPC;
CurrentState = "盘点中";
return;
}
else //二次验证通过
{
_verifyCts?.Cancel();
Log.Information($"验证成功");
//插入成功记录
await DataAdd(iCombineId, tagInfos, true);
return;
}
}
}
catch (Exception e)
{
Log.Error($"读结果准备写入时异常:{e.Message}");
CurrentState = "空闲";
}
}
/// <summary>
/// 插入记录
/// </summary>
/// <param name="iCombineId"></param>
/// <param name="tagInfos"></param>
/// <param name="isSuccess"></param>
private async Task DataAdd(string iCombineId, List<TagInfo> tagInfos, bool isSuccess)
{
var deviceInfo = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId);
var rfidInfo = rfidList.FirstOrDefault(x => x.deviceid == iCombineId);
var deviceConfig = appConfig.deviceInfoConfig.FirstOrDefault(x => x.Deviceid == iCombineId);
IsVerify = false;
deviceInfo.WriteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
deviceInfo.WriteStatus = isSuccess ? "成功" : "失败";
LastWriteState = isSuccess ? "成功" : "失败";
deviceInfo.RfidASCII = LastWrite;
deviceInfo.RfidEPC = LastRFIDEPC;
deviceInfo.SerialNo = (Convert.ToInt32(deviceInfo.SerialNo) + 1).ToString("D2");
//保存写入记录
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,
rfidepc = deviceInfo.RfidEPC,
rfidascii = 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 = "";
LastWrite = "";
await rfidInfo.Set_BeginIdentify();
await Task.Delay(100);
StartInventoryTimer(iCombineId, deviceConfig.InventoryIntervalSeconds.Value);
await rfidInfo.Set_HeartBeat(5);
CurrentState = "盘点中";
return;
}
#endregion
#region 数据流相关
/// <summary>
/// 开始订单初次流程
/// </summary>
/// <param name="recipient"></param>
/// <param name="real_Data"></param>
private void RefreshOrderNo(object recipient, Real_DataInfo real_Data)
{
//查询当前订单产线最大序列号
List<real_readdata> real_Readdatas = databaseService._helper.Query(x => x.orderno == real_Data.OrderNo && x.lineno == real_Data.LineNo);
string SNo = "";
if (real_Readdatas.Count > 0)
{
SNo = real_Readdatas.Max(x => x.serialno);
SNo = (Convert.ToInt32(SNo) + 1).ToString("D2");
}
else
{
SNo = "1";
}
Deviceinfo.FirstOrDefault(x => x.Deviceid == real_Data.Deviceid).SerialNo = Convert.ToInt32(SNo).ToString("D2");
Deviceinfo.FirstOrDefault(x => x.Deviceid == real_Data.Deviceid).OrderNo = real_Data.OrderNo;
Deviceinfo.FirstOrDefault(x => x.Deviceid == real_Data.Deviceid).ProductType = real_Data.ProductType;
Deviceinfo.FirstOrDefault(x => x.Deviceid == real_Data.Deviceid).LineNo = real_Data.LineNo;
Deviceinfo.FirstOrDefault(x => x.Deviceid == real_Data.Deviceid).RfidCount = real_Data.RfidCount;
Deviceinfo.FirstOrDefault(x => x.Deviceid == real_Data.Deviceid).NextProductNo = real_Data.NextProductNo;
rfidList.FirstOrDefault(x => x.deviceid == real_Data.Deviceid).Set_BeginIdentify().GetAwaiter().GetResult();
IsVerify = false;
CurrentState = "盘点中";
// 启动定时盘点
var deviceConfig = appConfig.deviceInfoConfig.FirstOrDefault(x => x.Deviceid == real_Data.Deviceid);
if (deviceConfig?.InventoryIntervalSeconds > 0)
{
StartInventoryTimer(real_Data.Deviceid, deviceConfig.InventoryIntervalSeconds.Value);
}
}
/// <summary>
/// 停止盘点消息
/// </summary>
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);
}
CurrentState = "空闲";
}
/// <summary>
/// 初次加载设备信息
/// </summary>
private void LoadDeviceInfo()
{
List<DeviceInfoConfig> DeviceInfos = appConfig.deviceInfoConfig.Where(x => x.Collectid == appConfig.StationCode && x.Deleteflag == 0).ToList();
List<Real_DataInfo> real_DataInfos = new List<Real_DataInfo>();
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);
}
});
}
/// <summary>
/// 添加数据的方法
/// </summary>
/// <param name="iCombineId"></param>
/// <param name="tagInfos"></param>
public void AddRFIDData(string iCombineId, List<TagInfo> tagInfos)
{
Task.Run(() =>
{
App.Current.Dispatcher.Invoke(() =>
{
RFIDHistoryRecords.Insert(0, new RFIDRecord
{
OrderNumber = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).OrderNo,
LineNumber = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).LineNo,
ProductType = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).ProductType,
OriginalEPC = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).RfidEPC,
RFIDCode = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).RfidASCII,
ReadTime = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).ReadTime,
WriteTime = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).ReadTime,
WriteStatus = Deviceinfo.FirstOrDefault(x => x.Deviceid == iCombineId).WriteStatus // 模拟1条失败数据
});
});
});
}
#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<List<RfidAbsractFactory>> SplitListIntoBatches(List<RfidAbsractFactory> sourceList, int batchSize)
{
var batches = new List<List<RfidAbsractFactory>>();
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<RfidAbsractFactory> batch)
{
// 并行检测当前批次的设备(可选,根据接口性能调整)
var detectionTasks = batch.Select(device => DetectSingleDeviceAsync(device));
// 等待当前批次所有设备检测完成
await Task.WhenAll(detectionTasks);
}
/// <summary>
/// 重连核心算法
/// </summary>
/// <param name="device"></param>
/// <returns></returns>
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);
}
}
/// <summary>
/// 无限重连
/// </summary>
/// <param name="device"></param>
/// <returns></returns>
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 定时盘点
/// <summary>
/// 启动定时盘点
/// </summary>
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");
}
/// <summary>
/// 停止定时盘点
/// </summary>
private void StopInventoryTimer(string deviceId)
{
if (_inventoryTimers.TryGetValue(deviceId, out var timer))
{
timer.Stop();
_inventoryTimers.Remove(deviceId);
Log.Information($"设备 {deviceId} 停止定时盘点");
}
}
/// <summary>
/// 定时盘点回调
/// </summary>
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);
rfid.Set_BeginIdentify().GetAwaiter().GetResult();
}
}
catch (Exception ex)
{
Log.Error($"设备 {deviceId} 定时盘点异常: {ex.Message}");
}
}
#endregion
}
}