diff --git a/DOAN.ServiceCore/DoanBackgroundService2.cs b/DOAN.ServiceCore/DoanBackgroundService2.cs
index e2b3d72..b5b784e 100644
--- a/DOAN.ServiceCore/DoanBackgroundService2.cs
+++ b/DOAN.ServiceCore/DoanBackgroundService2.cs
@@ -27,20 +27,21 @@ namespace DOAN.ServiceCore
notificationHubContext = hubContext;
}
- public Task StartAsync(CancellationToken cancellationToken)
+ public async Task StartAsync(CancellationToken cancellationToken)
{
- Console.WriteLine($"PCL定时任务开启!");
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PCL定时任务开启!");
pLCTool = new PLCTool();
- pLCTool.ConnectPLC();
+ bool isConnected = pLCTool.ConnectPLC();
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC初始连接状态: {(isConnected ? "成功" : "失败")}");
// 初始化料架层和点位表数据
- RefreshData(null);
+ await RefreshData(null);
// 启动后台任务
_executingTask = ExecuteAsync(_cancellationTokenSource.Token);
// 设置定时器每10分钟刷新一次料架层和点位表 .FromMinutes(1)
- refreshTimer = new Timer(RefreshData, null, TimeSpan.Zero, TimeSpan.FromMinutes(10));
+ refreshTimer = new Timer(RefreshDataTimerCallback, null, TimeSpan.Zero, TimeSpan.FromMinutes(10));
- return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask;
+ await (_executingTask.IsCompleted ? _executingTask : Task.CompletedTask);
}
public async Task StopAsync(CancellationToken cancellationToken)
@@ -78,21 +79,38 @@ namespace DOAN.ServiceCore
///
private async Task ExecuteAsync(CancellationToken stoppingToken)
{
- Console.WriteLine($"PCL定时任务ExecuteAsync!");
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PCL定时任务ExecuteAsync!");
try
{
while (!stoppingToken.IsCancellationRequested)
{
- // 读取PLC I/O状态(12个字节)
+ // 检查PLC连接状态,如果断开则尝试重连
+ if (!pLCTool.IsConnected())
+ {
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 检测到PLC连接断开,尝试重连...");
+ // 由于PLCTool中的ReconnectPLC是无限重连的,这里不需要额外的延迟
+ if (!pLCTool.ReconnectPLC())
+ {
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC重连失败,跳过本次循环");
+ continue;
+ }
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC重连成功");
+ }
+
+ // 读取PLC I/O状态(13个字节)
byte[] plcSensorValues;
try
{
// 读取1开头100,101。。。等全部地址数据
plcSensorValues = pLCTool.ReadAllValue("VB100", 13);
+ if (plcSensorValues != null)
+ {
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 成功读取PLC数据,字节数: {plcSensorValues.Length}");
+ }
}
catch (Exception ex)
{
- Console.WriteLine($"读取PLC数据异常: {ex.Message}\n堆栈跟踪: {ex.StackTrace}");
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 读取PLC数据异常: {ex.Message}\n堆栈跟踪: {ex.StackTrace}");
continue;
}
@@ -334,12 +352,12 @@ namespace DOAN.ServiceCore
}
catch (OperationCanceledException)
{
- Console.WriteLine("任务已取消");
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 任务已取消");
}
catch (Exception ex)
{
Console.WriteLine(
- $"DoanBackGround线程ExecuteAsync异常: {ex.Message}\n堆栈跟踪: {ex.StackTrace}"
+ $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] DoanBackGround线程ExecuteAsync异常: {ex.Message}\n堆栈跟踪: {ex.StackTrace}"
);
}
}
@@ -348,22 +366,53 @@ namespace DOAN.ServiceCore
/// 刷新料架层和点位表数据
///
///
- private async void RefreshData(object state)
+ private async Task RefreshData(object state)
{
try
{
- Console.WriteLine($"刷新料架点位和按钮");
- storagelocationList = await Context.Queryable().ToListAsync();
- pointPositionList = await Context.Queryable().ToListAsync();
- buttonPositionList = await Context.Queryable().ToListAsync();
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 开始刷新料架点位和按钮数据");
+ // 检查PLC连接状态,如果断开则尝试重连
+ if (!pLCTool.IsConnected())
+ {
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 检测到PLC连接断开,尝试重连...");
+ // 由于PLCTool中的ReconnectPLC是无限重连的,这里不需要额外的处理
+ if (!pLCTool.ReconnectPLC())
+ {
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC重连失败,但继续执行刷新操作");
+ }
+ else
+ {
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC重连成功");
+ }
+ }
+
+ var newStoragelocationList = await Context.Queryable().ToListAsync();
+ var newPointPositionList = await Context.Queryable().ToListAsync();
+ var newButtonPositionList = await Context.Queryable().ToListAsync();
+
+ // 原子性地更新数据
+ storagelocationList = newStoragelocationList;
+ pointPositionList = newPointPositionList;
+ buttonPositionList = newButtonPositionList;
+
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 刷新料架点位和按钮完成: 料架{storagelocationList.Count}个, 点位{pointPositionList.Count}个, 按钮{buttonPositionList.Count}个");
}
catch (Exception ex)
{
- Console.WriteLine($"刷新数据异常: {ex.Message}\n堆栈跟踪: {ex.StackTrace}");
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 刷新数据异常: {ex.Message}\n堆栈跟踪: {ex.StackTrace}");
// 如果刷新失败,保持现有数据不变
}
}
+ ///
+ /// 用于Timer回调的适配器方法
+ ///
+ ///
+ private void RefreshDataTimerCallback(object state)
+ {
+ _ = RefreshData(state); // 不等待异步操作完成,避免阻塞Timer
+ }
+
///
/// 更新料架位置的箱子数量并记录库存日志
///
@@ -404,16 +453,24 @@ namespace DOAN.ServiceCore
{
try
{
+ // 取消令牌源
_cancellationTokenSource.Cancel();
- _executingTask?.GetAwaiter().GetResult(); // 确保任务完成
+
+ // 等待任务完成,但设置超时以避免死锁
+ if (_executingTask != null)
+ {
+ Task.WaitAny(_executingTask, Task.Delay(5000)); // 最多等待5秒
+ }
+
+ // 释放资源
_cancellationTokenSource.Dispose();
refreshTimer?.Change(Timeout.Infinite, Timeout.Infinite);
refreshTimer?.Dispose();
- pLCTool.ConnectClose();
+ pLCTool?.ConnectClose();
}
catch (Exception ex)
{
- Console.WriteLine("DoanBackGround线程Dispose异常:" + ex.Message);
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] DoanBackGround线程Dispose异常: {ex.Message}");
}
}
}
diff --git a/Infrastructure/PLC/PLCTool.cs b/Infrastructure/PLC/PLCTool.cs
index 0e82fe7..6f4f63d 100644
--- a/Infrastructure/PLC/PLCTool.cs
+++ b/Infrastructure/PLC/PLCTool.cs
@@ -14,6 +14,8 @@ namespace DOAN.Infrastructure.PLC
// 私有连接对象
private SiemensS7Net siemensTcpNet = null;
private readonly string plcAddress;
+ private const int ReconnectDelayMs = 5000;
+
public PLCTool()
{
plcAddress = AppSettings.GetConfig("PLCConfig:Address");
@@ -34,21 +36,77 @@ namespace DOAN.Infrastructure.PLC
var connect = siemensTcpNet.ConnectServer();
if (connect.IsSuccess)
{
- Console.WriteLine($"PLC连接成功,地址{plcAddress}");
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC连接成功,地址{plcAddress}");
return true;
}
else
{
- Console.WriteLine($"PLC连接失败,地址{plcAddress}: {connect.Message}");
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC连接失败,地址{plcAddress}: {connect.Message}");
return false;
}
}
catch (Exception e)
{
- Console.WriteLine($"PLC连接失败,地址{plcAddress}: {e.Message}");
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC连接失败,地址{plcAddress}: {e.Message}");
return false;
}
}
+
+ ///
+ /// 检查PLC连接状态
+ ///
+ /// 连接正常返回true,否则返回false
+ public bool IsConnected()
+ {
+ try
+ {
+ // 尝试读取一个字节来检查连接状态
+ var result = siemensTcpNet.Read("VB100", 1);
+ if (!result.IsSuccess)
+ {
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC连接状态检查失败: {result.Message}");
+ }
+ return result.IsSuccess;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC连接状态检查异常: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 重连PLC
+ ///
+ /// 重连成功返回true,失败返回false
+ public bool ReconnectPLC()
+ {
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 尝试重新连接PLC...");
+
+ // 先关闭现有连接
+ ConnectClose();
+
+ // 重新创建PLC对象
+ this.siemensTcpNet = new SiemensS7Net(SiemensPLCS.S200Smart, plcAddress)
+ {
+ ConnectTimeOut = 5000
+ };
+
+ // 持续尝试重新连接,直到成功
+ int attempt = 0;
+ while (true)
+ {
+ attempt++;
+ if (ConnectPLC())
+ {
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC重连成功,尝试次数: {attempt}");
+ return true;
+ }
+
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC重连失败,尝试次数: {attempt}");
+ System.Threading.Thread.Sleep(ReconnectDelayMs);
+ }
+ }
///
/// 向PLC写入单个bit值
@@ -87,7 +145,7 @@ namespace DOAN.Infrastructure.PLC
}
else
{
- Console.WriteLine($"PLC IO 取值失败,PLC地址{plcAddress},访问地址为{addr},地址个数为{length}");
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC IO 取值失败,PLC地址{plcAddress},访问地址为{addr},地址个数为{length}");
return null;
}
}