774 lines
31 KiB
C#
774 lines
31 KiB
C#
using AutoMapper;
|
||
using FATrace.Model;
|
||
using FATrace.WPLApp.Events;
|
||
using FATrace.WPLApp.ModelDto;
|
||
using FATrace.WPLApp.Models;
|
||
using HslCommunication;
|
||
using HslCommunication.Profinet.Keyence;
|
||
using Prism.Events;
|
||
using Prism.Mvvm;
|
||
using System.Collections.Concurrent;
|
||
using System.Windows;
|
||
using ComConfigHelper = FATrace.Com.ConfigHelper;
|
||
|
||
namespace FATrace.WPLApp.Services
|
||
{
|
||
/// <summary>
|
||
/// 数据服务
|
||
/// 产线PLC数据交互的服务
|
||
/// </summary>
|
||
public class DataServices : BindableBase, IDisposable
|
||
{
|
||
public DataServices(ILogService logService, IFreeSql freeSql, IEventAggregator eventAggregator, IMapper mapper, SysRunService sysRunService, CsvServices csvServices)
|
||
{
|
||
LogService = logService;
|
||
FreeSql = freeSql;
|
||
EventAggregator = eventAggregator;
|
||
Mapper = mapper;
|
||
SysRunService = sysRunService;
|
||
CsvServices = csvServices;
|
||
LineSglModel = new LineSglModel();
|
||
LineSglModel.WeightScanCodeHandle += LineSglModel_WeightScanCodeHandle;
|
||
LineSglModel.BoxSprayCodeReqHandle += LineSglModel_BoxSprayCodeReqHandle;
|
||
LineSglModel.BoxScanCodeReqHandle += LineSglModel_BoxScanCodeReqHandle;
|
||
|
||
PLCLinkInitial();
|
||
StartPlcScan();
|
||
//var DD= RevData("DYG05030013,20250923,802,3,01,0001,");
|
||
|
||
//var dd1= RevData("DYG05030013,251111,10193,6,01,3");
|
||
//var dd2 = RevData("DYG05030013,251111,2116,6,01,3");
|
||
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
///外箱喷码请求
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
private void LineSglModel_BoxSprayCodeReqHandle(object? sender, string e)
|
||
{
|
||
//首先复位PLC信号
|
||
var SetValueResult = KeyencePlcMcNet!.Write("D1100", (Int16)0);
|
||
if (!SetValueResult.IsSuccess)
|
||
{
|
||
//写入失败,重新写入
|
||
LogService.Warn("外箱喷码-复位PLC信号失败-执行两次写入0");
|
||
KeyencePlcMcNet!.Write("D1100", (Int16)0);
|
||
KeyencePlcMcNet!.Write("D1100", (Int16)0);
|
||
}
|
||
|
||
//获取箱子喷码的数据+A
|
||
var ReqData = FreeSql.Select<LineTempCode>()
|
||
.OrderByDescending(a => a.Id)
|
||
.Limit(1)
|
||
.ToList();
|
||
|
||
//无数据再重试一次
|
||
if (ReqData.Count <= 0)
|
||
{
|
||
try
|
||
{
|
||
Thread.Sleep(50);
|
||
}
|
||
catch
|
||
{
|
||
}
|
||
|
||
ReqData = FreeSql.Select<LineTempCode>()
|
||
.OrderByDescending(a => a.Id)
|
||
.Limit(1)
|
||
.ToList();
|
||
|
||
LogService.Warn("外箱喷码-不存在请求的数据-重试一次");
|
||
}
|
||
|
||
if (ReqData.Count > 0)
|
||
{
|
||
var CodeItem = ReqData.FirstOrDefault();
|
||
|
||
var BoxSprayCodeSource = CodeItem!.Code + ",A";
|
||
var BoxSprayCodeRev = RevData(BoxSprayCodeSource);
|
||
|
||
BoxSprayCode = BoxSprayCodeSource;
|
||
var Result = KeyencePlcMcNet.Write("D1150", BoxSprayCodeRev);
|
||
if (!Result.IsSuccess)
|
||
{
|
||
//写入失败,重新写入
|
||
LogService.Warn("外箱喷码-写入条码 D1150 PLC信号失败-执行两次写入条码");
|
||
KeyencePlcMcNet.Write("D1150", BoxSprayCodeRev);
|
||
}
|
||
Console.WriteLine($"外箱喷码:{BoxSprayCodeSource}-发送OK");
|
||
|
||
}
|
||
else
|
||
{
|
||
//不存在请求的数据,报错
|
||
KeyencePlcMcNet.Write("D1102", (Int16)1);
|
||
Console.WriteLine($"外箱喷码:不存在请求的数据");
|
||
AddAlarm("外箱喷码", "不存在请求的数据");
|
||
LogService.Warn("外箱喷码-不存在请求的数据");
|
||
}
|
||
|
||
|
||
Task.Run(async () =>
|
||
{
|
||
await Task.Delay(1000).ConfigureAwait(false);
|
||
var Data = KeyencePlcMcNet!.Write("D1102", (Int16)0);
|
||
if (!Data.IsSuccess)
|
||
{
|
||
//写入失败,重新写入
|
||
LogService.Warn("外箱喷码-复位D1102PLC信号失败-执行两次写入0");
|
||
KeyencePlcMcNet!.Write("D1102", (Int16)0);
|
||
KeyencePlcMcNet!.Write("D1102", (Int16)0);
|
||
}
|
||
|
||
//KeyencePlcMcNet.Write("D1150", new Int16[30]);
|
||
});
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 外箱扫描码请求
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
private void LineSglModel_BoxScanCodeReqHandle(object? sender, string e)
|
||
{
|
||
//首先复位PLC信号
|
||
var SetValueResult = KeyencePlcMcNet!.Write("D1200", (Int16)0);
|
||
if (!SetValueResult.IsSuccess)
|
||
{
|
||
//写入失败,重新写入
|
||
LogService.Warn("外箱扫描码请求-复位PLC信号失败-执行两次写入0");
|
||
KeyencePlcMcNet!.Write("D1200", (Int16)0);
|
||
KeyencePlcMcNet!.Write("D1200", (Int16)0);
|
||
}
|
||
|
||
string BoxScanCode = string.Empty;
|
||
BoxScanCode = this.BoxScanCode;
|
||
|
||
if (!string.IsNullOrEmpty(BoxScanCode))
|
||
{
|
||
var IsExist = FreeSql.Select<RawProUse>().Where(a => a.BoxCode == BoxScanCode);
|
||
//var IsExist = FreeSql.Select<RawProUse>().Where(a => a.BoxCode!.Contains(BoxScanCode));// 内包和外包一样的条码测试用 ???????????????
|
||
if (IsExist.Count() > 0)
|
||
{
|
||
//存在条码数据 OK,返回PLC结果
|
||
var DataResult = KeyencePlcMcNet!.Write("D1210", (Int16)1);
|
||
if (!DataResult.IsSuccess)
|
||
{
|
||
//写入失败,重新写入
|
||
LogService.Warn("外箱扫描码请求-置位D1210PLC信号失败-执行两次写入1");
|
||
KeyencePlcMcNet!.Write("D1210", (Int16)1);
|
||
KeyencePlcMcNet!.Write("D1210", (Int16)1);
|
||
}
|
||
|
||
Console.WriteLine($"外箱扫描码:{BoxScanCode}-存在找到");
|
||
// 加入队列
|
||
//EnqueueMessage(WeightScanCode);
|
||
LogService.Info($"外箱扫描码:{BoxScanCode}");
|
||
|
||
//删除临时的队列条码数据
|
||
var DeleteResult = FreeSql.Delete<LineTempCode>()
|
||
.Where(p => BoxScanCode.Contains(p.Code!))
|
||
.ExecuteAffrows();
|
||
if (DeleteResult > 0)
|
||
{
|
||
Console.WriteLine($"外箱扫描码:{BoxScanCode}-删除临时队列数据成功");
|
||
LogService.Info($"外箱扫描码:{BoxScanCode}-删除临时队列数据成功");
|
||
try { EventAggregator?.GetEvent<DashboardRefreshEvent>()?.Publish(true); } catch { }
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"外箱扫描码:{BoxScanCode}-删除临时队列数据失败");
|
||
LogService.Info($"外箱扫描码:{BoxScanCode}-删除临时队列数据失败");
|
||
AddAlarm("外箱扫描", $"{BoxScanCode}-删除临时队列数据失败");
|
||
try { EventAggregator?.GetEvent<DashboardRefreshEvent>()?.Publish(true); } catch { }
|
||
}
|
||
|
||
var UpdatedResult = FreeSql.Update<RawProUse>()
|
||
.Set(p => p.OutTime, DateTime.Now)
|
||
.Where(p => p.BoxCode == BoxScanCode)//外箱二维码就是外箱扫描码
|
||
.ExecuteUpdated();
|
||
if (UpdatedResult.Count() > 0)
|
||
{
|
||
var Data = CsvServices.ExportSingle(Mapper.Map<RawProUserCsvDto>(UpdatedResult.First()), true);
|
||
if (Data != null && !string.IsNullOrEmpty(Data))
|
||
{
|
||
Console.WriteLine($"外箱扫描码:{BoxScanCode} CSV文件生成");
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"外箱扫描码:{BoxScanCode} CSV文件失败!");
|
||
}
|
||
|
||
Console.WriteLine($"外箱扫描码:{BoxScanCode}更新时间成功");
|
||
LogService.Info($"外箱扫描码:{BoxScanCode}更新时间成功");
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"外箱扫描码:{BoxScanCode}更新时间失败");
|
||
LogService.Warn($"外箱扫描码:{BoxScanCode}更新时间失败");
|
||
AddAlarm("外箱扫描", $"{BoxScanCode}更新时间失败");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//不存在条码数据 NG,返回PLC结果报警
|
||
KeyencePlcMcNet!.Write("D1210", (Int16)2);
|
||
|
||
Console.WriteLine($"外箱扫描码:{BoxScanCode}-未发现外箱扫码数据在数据库中");
|
||
LogService.Warn($"外箱扫描码:{BoxScanCode}-未发现外箱扫码数据在数据库中");
|
||
AddAlarm("外箱扫描", $"{BoxScanCode}-未发现外箱扫码数据在数据库中");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"外箱扫描码为空");
|
||
LogService.Warn("外箱扫描码为空");
|
||
AddAlarm("外箱扫描", "外箱扫描码为空");
|
||
}
|
||
|
||
Task.Run(async () =>
|
||
{
|
||
await Task.Delay(1200).ConfigureAwait(false);
|
||
var DataResult = KeyencePlcMcNet!.Write("D1210", (Int16)0);
|
||
if (!DataResult.IsSuccess)
|
||
{
|
||
//写入失败,重新写入
|
||
LogService.Warn("外箱扫描码请求-复位D1210PLC信号失败-执行两次写入0");
|
||
KeyencePlcMcNet!.Write("D1210", (Int16)0);
|
||
KeyencePlcMcNet!.Write("D1210", (Int16)0);
|
||
}
|
||
|
||
KeyencePlcMcNet.Write("D1250", new Int16[30]);
|
||
});
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 称重 内包扫描码请求数据
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
private void LineSglModel_WeightScanCodeHandle(object? sender, string e)
|
||
{
|
||
//首先复位PLC信号
|
||
var SetValueResult = KeyencePlcMcNet!.Write("D1000", (Int16)0);
|
||
if (!SetValueResult.IsSuccess)
|
||
{
|
||
//写入失败,重新写入
|
||
LogService.Warn("称重扫描码-复位PLC信号失败-执行两次写入0");
|
||
KeyencePlcMcNet!.Write("D1000", (Int16)0);
|
||
KeyencePlcMcNet!.Write("D1000", (Int16)0);
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(WeightScanCode))
|
||
{
|
||
var IsExist = FreeSql.Select<RawProUse>().Where(a => a.InBagCode == WeightScanCode);
|
||
if (IsExist.Count() > 0)
|
||
{
|
||
//存在条码数据 OK,返回PLC结果
|
||
var SetValueResult1 = KeyencePlcMcNet!.Write("D1010", (Int16)1);
|
||
if (!SetValueResult1.IsSuccess)
|
||
{
|
||
//写入失败,重新写入
|
||
LogService.Warn("称重扫描码-置位数据D1010 PLC信号失败-执行两次写入1");
|
||
KeyencePlcMcNet!.Write("D1010", (Int16)1);
|
||
KeyencePlcMcNet!.Write("D1010", (Int16)1);
|
||
}
|
||
|
||
Console.WriteLine($"称重扫描码:{WeightScanCode}");
|
||
//// 加入队列
|
||
//EnqueueMessage(WeightScanCode);
|
||
LogService.Info($"称重扫描码:{WeightScanCode}");
|
||
|
||
try
|
||
{
|
||
var delAff = FreeSql.Delete<LineTempCode>()
|
||
.Where(p => p.Id > 0)
|
||
.ExecuteAffrows();
|
||
|
||
var insAff = FreeSql.Insert<LineTempCode>(new LineTempCode()
|
||
{
|
||
Code = WeightScanCode
|
||
}).ExecuteAffrows();
|
||
|
||
if (insAff <= 0)
|
||
{
|
||
LogService.Warn($"称重扫描码:{WeightScanCode} 写入临时表 LineTempCode 失败(affrows={insAff}),delAff={delAff}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogService.Error($"称重扫描码:{WeightScanCode} 写入临时表 LineTempCode 异常: {ex}");
|
||
}
|
||
try
|
||
{
|
||
EventAggregator?.GetEvent<DashboardRefreshEvent>()?.Publish(true);
|
||
}
|
||
catch
|
||
{
|
||
|
||
}
|
||
|
||
var Result = FreeSql.Update<RawProUse>()
|
||
.Set(p => p.WeightScanTime, DateTime.Now)
|
||
.Where(p => p.InBagCode == WeightScanCode)//内箱二维码就是称重扫描码
|
||
.ExecuteAffrows();
|
||
if (Result > 0)
|
||
{
|
||
Console.WriteLine($"称重扫描码:{WeightScanCode}更新时间成功");
|
||
LogService.Info($"称重扫描码:{WeightScanCode}更新时间成功");
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"称重扫描码:{WeightScanCode}更新时间失败");
|
||
LogService.Warn($"称重扫描码:{WeightScanCode}更新时间失败");
|
||
AddAlarm("称重扫描", $"{WeightScanCode}更新时间失败");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//不存在条码数据 NG,返回PLC结果报警
|
||
KeyencePlcMcNet!.Write("D1010", (Int16)2);
|
||
|
||
Console.WriteLine($"称重扫描码:{WeightScanCode}-未发现内包扫码数据在数据库中");
|
||
LogService.Warn($"称重扫描码:{WeightScanCode}-未发现内包扫码数据在数据库中");
|
||
AddAlarm("称重扫描", $"{WeightScanCode}-未发现内包扫码数据在数据库中");
|
||
}
|
||
|
||
Task.Run(async () =>
|
||
{
|
||
await Task.Delay(1200).ConfigureAwait(false);
|
||
|
||
//KeyencePlcMcNet!.Write("D1010", (Int16)0);
|
||
var SetValueResult2 = KeyencePlcMcNet!.Write("D1010", (Int16)0);
|
||
if (!SetValueResult2.IsSuccess)
|
||
{
|
||
//写入失败,重新写入
|
||
LogService.Warn("称重扫描码-复位数据D1010 PLC信号失败-执行两次写入0");
|
||
KeyencePlcMcNet!.Write("D1010", (Int16)0);
|
||
}
|
||
|
||
KeyencePlcMcNet.Write("D1050", new Int16[30]);
|
||
});
|
||
|
||
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"称重扫描码为空");
|
||
LogService.Warn("称重扫描码为空");
|
||
AddAlarm("称重扫描", "称重扫描码为空");
|
||
}
|
||
|
||
}
|
||
|
||
public ILogService LogService { get; }
|
||
public IFreeSql FreeSql { get; }
|
||
public IEventAggregator EventAggregator { get; }
|
||
public IMapper Mapper { get; }
|
||
public SysRunService SysRunService { get; }
|
||
public CsvServices CsvServices { get; }
|
||
|
||
private bool _plcConnected;
|
||
public bool PlcConnected
|
||
{
|
||
get { return _plcConnected; }
|
||
private set { _plcConnected = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 基恩士通信组件
|
||
/// </summary>
|
||
private KeyenceMcNet KeyencePlcMcNet { get; set; } = null;
|
||
|
||
private string _WeightScanCode;
|
||
/// <summary>
|
||
/// 称重扫描码
|
||
/// </summary>
|
||
public string WeightScanCode
|
||
{
|
||
get { return _WeightScanCode; }
|
||
set { _WeightScanCode = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
private string _BoxScanCode;
|
||
/// <summary>
|
||
/// 外箱喷码扫码数据
|
||
/// </summary>
|
||
public string BoxScanCode
|
||
{
|
||
get { return _BoxScanCode; }
|
||
set { _BoxScanCode = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
private string _boxSprayCode;
|
||
/// <summary>
|
||
/// 最近一次下发给 PLC 的外箱喷码数据(源字符串,不做 RevData 对调处理)
|
||
/// </summary>
|
||
public string BoxSprayCode
|
||
{
|
||
get { return _boxSprayCode; }
|
||
set { _boxSprayCode = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
// 扫描控制
|
||
private CancellationTokenSource? _plcScanCts;
|
||
private Task? _plcScanTask;
|
||
private volatile bool _isScanning = false;
|
||
public bool IsScanning => _isScanning;
|
||
private bool _disposed = false;
|
||
|
||
|
||
/// <summary>
|
||
/// 产线信号模型
|
||
/// </summary>
|
||
public LineSglModel LineSglModel { get; set; }
|
||
|
||
/// <summary>
|
||
/// PLC通信初始化
|
||
/// </summary>
|
||
/// <param name="PLCIp"></param>
|
||
/// <param name="Port"></param>
|
||
private void PLCLinkInitial()
|
||
{
|
||
try
|
||
{
|
||
// 读取 PLC 配置(大小写敏感,采用公共 ConfigHelper 并提供默认值)
|
||
string PLCIP = ComConfigHelper.GetStringOrDefault("PLCIP", "192.0.1.10");
|
||
int Port = ComConfigHelper.GetIntOrDefault("PLCPort", 5000);
|
||
try { SysRunService.PlcIp = PLCIP; SysRunService.PlcPort = Port; } catch { }
|
||
|
||
//PLC通信的连接
|
||
KeyencePlcMcNet = new KeyenceMcNet();
|
||
KeyencePlcMcNet.IpAddress = PLCIP;
|
||
KeyencePlcMcNet.Port = Port;
|
||
KeyencePlcMcNet.ConnectClose();
|
||
|
||
KeyencePlcMcNet.ConnectTimeOut = 3000; // 连接3秒超时
|
||
OperateResult connect = KeyencePlcMcNet.ConnectServer();
|
||
if (connect.IsSuccess)//初始连接状态的显示判断
|
||
{
|
||
Console.WriteLine($"PLC-连接 OK");
|
||
LogService.Error($"PLC-连接 OK");
|
||
PlcConnected = true;
|
||
try { SysRunService.PlcLinkState = true; SysRunService.SysRunState.ComState = 1; SysRunService.SysRunState.ComMsg = "正常"; } catch { }
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show(connect.Message + Environment.NewLine + "ErrorCode: " + connect.ErrorCode);
|
||
PlcConnected = false;
|
||
try { SysRunService.PlcLinkState = false; SysRunService.SysRunState.ComState = 2; SysRunService.SysRunState.ComMsg = "连接失败"; } catch { }
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogService.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString()));
|
||
//insertLogToDBDelegate.BeginInvoke(1, "UpdateUIMethod异常", ex.Message.ToString() + ex.StackTrace.Substring(ex.StackTrace.Length - 40, 40), null, null);
|
||
PlcConnected = false;
|
||
try { SysRunService.PlcLinkState = false; SysRunService.SysRunState.ComState = 2; SysRunService.SysRunState.ComMsg = "异常"; } catch { }
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 启动PLC扫描。若已在扫描则忽略重复启动。
|
||
/// </summary>
|
||
/// <param name="scanPeriod">扫描周期,若为 null 则读取 App.config 的 PLCScan(毫秒),默认600ms</param>
|
||
/// <param name="externalToken">可选:外部取消令牌,用于联动取消</param>
|
||
public void StartPlcScan(TimeSpan? scanPeriod = null, CancellationToken externalToken = default)
|
||
{
|
||
if (_isScanning) return;
|
||
|
||
var period = scanPeriod ?? ComConfigHelper.GetTimeSpanOrDefault("PLCScan", TimeSpan.FromMilliseconds(600));
|
||
|
||
_plcScanCts = CancellationTokenSource.CreateLinkedTokenSource(externalToken);
|
||
var token = _plcScanCts.Token;
|
||
_isScanning = true;
|
||
|
||
try { SysRunService.PlcScanState = 1; } catch { }
|
||
|
||
_plcScanTask = Task.Run(() => PlcScanLoopAsync(period, token), token);
|
||
_plcScanTask.ContinueWith(t =>
|
||
{
|
||
try
|
||
{
|
||
if (t.IsFaulted)
|
||
{
|
||
try { SysRunService.PlcScanState = 2; } catch { }
|
||
try { LogService.Error($"PLC扫描任务异常退出: {t.Exception}"); } catch { }
|
||
}
|
||
else if (t.IsCanceled)
|
||
{
|
||
try { SysRunService.PlcScanState = 0; } catch { }
|
||
}
|
||
else
|
||
{
|
||
try { SysRunService.PlcScanState = 0; } catch { }
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
}
|
||
}, TaskScheduler.Default);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止PLC扫描,等待后台任务结束。
|
||
/// </summary>
|
||
public async Task StopPlcScanAsync()
|
||
{
|
||
try
|
||
{
|
||
if (_plcScanCts != null)
|
||
{
|
||
_plcScanCts.Cancel();
|
||
}
|
||
if (_plcScanTask != null)
|
||
{
|
||
try { await _plcScanTask.ConfigureAwait(false); } catch { /* ignore */ }
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
_isScanning = false;
|
||
_plcScanTask = null;
|
||
_plcScanCts?.Dispose();
|
||
_plcScanCts = null;
|
||
|
||
try { SysRunService.PlcScanState = 0; } catch { }
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 称重扫描条码OperateResult结果
|
||
/// </summary>
|
||
private OperateResult<string> OperateResultWeightScanCode { get; set; }
|
||
|
||
/// <summary>
|
||
/// 称重扫描条码OK信号
|
||
/// </summary>
|
||
private OperateResult<Int16> OperateResultWeightScanSgl { get; set; }
|
||
|
||
/// <summary>
|
||
/// 外箱喷码请求信号
|
||
/// </summary>
|
||
private OperateResult<Int16> OperateResultBoxSprayCodeReqSgl { get; set; }
|
||
|
||
/// <summary>
|
||
/// 外箱扫码条码OperateResult结果
|
||
/// </summary>
|
||
private OperateResult<string> OperateResultBoxScanCode { get; set; }
|
||
|
||
/// <summary>
|
||
/// 外箱扫码请求信号
|
||
/// </summary>
|
||
private OperateResult<Int16> OperateResultBoxScanSgl { get; set; }
|
||
|
||
/// <summary>
|
||
/// 扫描循环:按周期轮询PLC寄存器并更新模型。
|
||
/// - 读取配置项 Addr_WeightPhotoEnable(默认 M100)为布尔量
|
||
/// - 读失败时尝试重连一次
|
||
/// </summary>
|
||
private async Task PlcScanLoopAsync(TimeSpan period, CancellationToken token)
|
||
{
|
||
string addrWeight = ComConfigHelper.GetStringOrDefault("Addr_WeightPhotoEnable", "M100");
|
||
bool? lastWeight = null;
|
||
|
||
while (!token.IsCancellationRequested)
|
||
{
|
||
try
|
||
{
|
||
if (KeyencePlcMcNet == null)
|
||
{
|
||
PLCLinkInitial();
|
||
}
|
||
|
||
//内包扫码
|
||
OperateResultWeightScanCode = KeyencePlcMcNet!.ReadString("D1050", 20);
|
||
if (OperateResultWeightScanCode.IsSuccess)
|
||
{
|
||
WeightScanCode = RevData(OperateResultWeightScanCode.Content).Replace("\r", "").Replace("\n", "").Trim();
|
||
}
|
||
OperateResultWeightScanSgl = KeyencePlcMcNet!.ReadInt16("D1000");
|
||
if (OperateResultWeightScanSgl.IsSuccess)
|
||
{
|
||
LineSglModel.WeightScanCodeEnable = OperateResultWeightScanSgl.Content;
|
||
}
|
||
|
||
//外箱喷码
|
||
OperateResultBoxSprayCodeReqSgl = KeyencePlcMcNet!.ReadInt16("D1100");
|
||
if (OperateResultBoxSprayCodeReqSgl.IsSuccess)
|
||
{
|
||
LineSglModel.BoxSprayCodeReqEnable = OperateResultBoxSprayCodeReqSgl.Content;
|
||
}
|
||
|
||
//外箱扫码
|
||
OperateResultBoxScanCode = KeyencePlcMcNet!.ReadString("D1250", 20);
|
||
if (OperateResultBoxScanCode.IsSuccess)
|
||
{
|
||
BoxScanCode = RevData(OperateResultBoxScanCode.Content).Replace("\r", "").Replace("\n", "").Trim();
|
||
//BoxScanCode = OperateResultBoxScanCode.Content.Replace("\r", "").Replace("\n", "");
|
||
}
|
||
OperateResultBoxScanSgl = KeyencePlcMcNet!.ReadInt16("D1200");
|
||
if (OperateResultBoxScanSgl.IsSuccess)
|
||
{
|
||
LineSglModel.BoxScanCodeEnable = OperateResultBoxScanSgl.Content;
|
||
|
||
PlcConnected = true;
|
||
try { SysRunService.PlcLinkState = true; SysRunService.SysRunState.ComState = 1; SysRunService.SysRunState.ComMsg = "正常"; } catch { }
|
||
}
|
||
else
|
||
{
|
||
PlcConnected = false;
|
||
try { SysRunService.PlcLinkState = false; SysRunService.SysRunState.ComState = 2; SysRunService.SysRunState.ComMsg = "连接失败"; } catch { }
|
||
}
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogService.Error($"PLC 扫描异常 Exception: {ex.ToString()}");
|
||
|
||
// 这里属于循环内部异常:循环仍会继续;
|
||
// 但若后续由于未捕获异常导致扫描任务退出,StartPlcScan 的 ContinueWith 也会做最终状态处理。
|
||
}
|
||
finally
|
||
{
|
||
try
|
||
{
|
||
await Task.Delay(period, token).ConfigureAwait(false);
|
||
}
|
||
catch (OperationCanceledException ex)
|
||
{
|
||
// 正常停止扫描时会触发取消,这里不应记录为错误。
|
||
// 不允许从 finally 中 break/return,
|
||
// 这里吞掉异常,循环将在下一次 while 判断时依据 token 自动退出
|
||
}
|
||
}
|
||
}
|
||
_isScanning = false;
|
||
|
||
// 退出循环:如果是正常取消,则 StopPlcScanAsync 会把状态置为停止;
|
||
// 若是外部未走 StopPlcScanAsync,而 token 已取消,也归为停止。
|
||
try
|
||
{
|
||
if (token.IsCancellationRequested) SysRunService.PlcScanState = 0;
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试重连 PLC(快速一次)
|
||
/// </summary>
|
||
private async Task TryReconnectAsync(CancellationToken token)
|
||
{
|
||
try
|
||
{
|
||
if (KeyencePlcMcNet == null) { PLCLinkInitial(); return; }
|
||
KeyencePlcMcNet.ConnectClose();
|
||
await Task.Delay(100, token).ConfigureAwait(false);
|
||
KeyencePlcMcNet.ConnectTimeOut = 3000;
|
||
var ret = KeyencePlcMcNet.ConnectServer();
|
||
if (!ret.IsSuccess)
|
||
{
|
||
LogService.Warn($"PLC重连失败: {ret.Message} (Code: {ret.ErrorCode})");
|
||
PlcConnected = false;
|
||
try { SysRunService.PlcLinkState = false; SysRunService.SysRunState.ComState = 2; SysRunService.SysRunState.ComMsg = "异常"; } catch { }
|
||
}
|
||
else { PlcConnected = true; try { SysRunService.PlcLinkState = true; SysRunService.SysRunState.ComState = 1; SysRunService.SysRunState.ComMsg = "正常"; } catch { } }
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogService.Error($"PLC重连异常: {ex.Message}");
|
||
PlcConnected = false;
|
||
try { SysRunService.PlcLinkState = false; SysRunService.SysRunState.ComState = 2; SysRunService.SysRunState.ComMsg = "异常"; } catch { }
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放资源,确保扫描停止并断开PLC连接。
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
if (_disposed) return;
|
||
_disposed = true;
|
||
try
|
||
{
|
||
// 停止扫描
|
||
try { StopPlcScanAsync().GetAwaiter().GetResult(); } catch { }
|
||
|
||
// 解除事件订阅(当前未订阅自定义事件,保留占位以便后续扩展)
|
||
// try { /* no event to unsubscribe */ } catch { }
|
||
|
||
// 关闭PLC连接
|
||
try { KeyencePlcMcNet?.ConnectClose(); } catch { }
|
||
}
|
||
finally
|
||
{
|
||
KeyencePlcMcNet = null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 把字符串对调
|
||
/// </summary>
|
||
public string RevData(string Code)
|
||
{
|
||
// 需求:PLC 字符串按“字”(2字节)进行高低字节对调,导致 ABCDEF -> BADCFE
|
||
// 纠正方式:按两个字符一组交换顺序,奇数长度保留最后一个字符
|
||
if (string.IsNullOrEmpty(Code)) return string.Empty;
|
||
|
||
// 去掉读取中可能包含的 '\0' 空字符
|
||
var src = Code.Replace("\0", string.Empty);
|
||
|
||
var sb = new System.Text.StringBuilder(src.Length);
|
||
for (int i = 0; i < src.Length; i += 2)
|
||
{
|
||
if (i + 1 < src.Length)
|
||
{
|
||
sb.Append(src[i + 1]);
|
||
sb.Append(src[i]);
|
||
}
|
||
else
|
||
{
|
||
// 奇数长度,最后一个字符原样保留
|
||
//sb.Append((char)0x0);
|
||
sb.Append('\0');
|
||
sb.Append(src[i]);
|
||
}
|
||
}
|
||
|
||
return sb.ToString();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 写入历史报警
|
||
/// </summary>
|
||
private void AddAlarm(string category, string message)
|
||
{
|
||
try
|
||
{
|
||
FreeSql.Insert(new HistoryAlarm
|
||
{
|
||
Category = category,
|
||
Message = message,
|
||
CreateTime = DateTime.Now
|
||
}).ExecuteAffrows();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogService.Error($"写入历史报警失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|