using FATrace.Com;
using HslCommunication;
using HslCommunication.Profinet.Keyence;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FATrace.OEMApp.Services
{
public class PLCDataService
{
///
/// 日志
///
private Logger Logger { get; set; } = LogManager.GetCurrentClassLogger();
public PLCDataService()
{
PLCLinkInitial();
StartPlcScan();
PadCodeAddress= ConfigHelper.GetStringOrDefault("PDAScanCode", "D1050");
}
///
/// PAD地址
///
private string PadCodeAddress { get; set; }
///
/// 扫码事件
///
public event EventHandler ScanCodeEventHandler;
///
/// PLC连接状态事件
///
public event EventHandler PlcConnectedEventHandler;
///
/// 基恩士通信组件
///
private KeyenceMcNet KeyencePlcMcNet { get; set; } = null;
private bool _PlcConnected;
///
/// PLC连接状态
///
public bool PlcConnected
{
get { return _PlcConnected; }
set
{
if (_PlcConnected != value)
{
if (value)
{
if (PlcConnectedEventHandler != null) PlcConnectedEventHandler.Invoke(this, "OK");
}
else
{
if (PlcConnectedEventHandler != null) PlcConnectedEventHandler.Invoke(this, "NG");
}
_PlcConnected = value;
}
}
}
private string _ScanCode = string.Empty;
///
/// 扫描结果
///
public string ScanCode
{
get { return _ScanCode; }
set
{
if (_ScanCode != value)
{
if (!string.IsNullOrEmpty(value))
{
if (ScanCodeEventHandler != null) ScanCodeEventHandler.Invoke(this, value);
}
else
{
Logger.Error($"PLC条码数据为空: 数据改变但是数据为空");
}
_ScanCode = value;
}
}
}
///
/// 扫描结果
///
private OperateResult OperateResultPDAScanCode { get; set; }
// 扫描控制
private CancellationTokenSource? _plcScanCts;
private Task? _plcScanTask;
private volatile bool _isScanning = false;
public bool IsScanning => _isScanning;
private bool _disposed = false;
///
/// PLC通信初始化
///
///
///
private void PLCLinkInitial()
{
try
{
// 读取 PLC 配置(大小写敏感,采用公共 ConfigHelper 并提供默认值)
string PLCIP = ConfigHelper.GetStringOrDefault("PLCIP", "192.0.1.10");
int Port = ConfigHelper.GetIntOrDefault("PLCPort", 5000);
//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");
PlcConnected = true;
}
else
{
MessageBox.Show(connect.Message + Environment.NewLine + "ErrorCode: " + connect.ErrorCode);
PlcConnected = false;
}
}
catch (Exception ex)
{
Logger.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;
}
}
///
/// 启动PLC扫描。若已在扫描则忽略重复启动。
///
/// 扫描周期,若为 null 则读取 App.config 的 PLCScan(毫秒),默认600ms
/// 可选:外部取消令牌,用于联动取消
public void StartPlcScan(TimeSpan? scanPeriod = null, CancellationToken externalToken = default)
{
if (_isScanning) return;
var period = scanPeriod ?? ConfigHelper.GetTimeSpanOrDefault("PLCScan", TimeSpan.FromMilliseconds(600));
_plcScanCts = CancellationTokenSource.CreateLinkedTokenSource(externalToken);
var token = _plcScanCts.Token;
_isScanning = true;
_plcScanTask = Task.Run(() => PlcScanLoopAsync(period, token), token);
}
///
/// 停止PLC扫描,等待后台任务结束。
///
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;
}
}
///
/// 扫描循环:按周期轮询PLC寄存器并更新模型。
/// - 读取配置项 Addr_WeightPhotoEnable(默认 M100)为布尔量
/// - 读失败时尝试重连一次
///
private async Task PlcScanLoopAsync(TimeSpan period, CancellationToken token)
{
string addrWeight = ConfigHelper.GetStringOrDefault("Addr_WeightPhotoEnable", "M100");
bool? lastWeight = null;
while (!token.IsCancellationRequested)
{
try
{
if (KeyencePlcMcNet == null)
{
PLCLinkInitial();
}
//内包扫码
OperateResultPDAScanCode = KeyencePlcMcNet!.ReadString(PadCodeAddress, 40);
if (OperateResultPDAScanCode.IsSuccess)
{
//ScanCode = RevData(OperateResultPDAScanCode.Content).Replace("\r", "").Replace("\n", "").Replace("\0", "").Trim();
ScanCode = (OperateResultPDAScanCode.Content).Replace("\r", "").Replace("\n", "").Replace("\0", "").Trim();
PlcConnected = true;
}
else
{
PlcConnected = false;
}
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
Logger.Error($"PLC 扫描异常: {ex}");
}
finally
{
try
{
await Task.Delay(period, token).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// 不允许从 finally 中 break/return,
// 这里吞掉异常,循环将在下一次 while 判断时依据 token 自动退出
}
}
}
_isScanning = false;
}
///
/// 尝试重连 PLC(快速一次)
///
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)
{
Logger.Warn($"PLC重连失败: {ret.Message} (Code: {ret.ErrorCode})");
PlcConnected = false;
}
else { PlcConnected = true; }
}
catch (Exception ex)
{
Logger.Error($"PLC重连异常: {ex.Message}");
PlcConnected = false;
}
}
///
/// 释放资源,确保扫描停止并断开PLC连接。
///
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;
}
}
///
/// 把字符串对调
///
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();
}
}
}