添加PLC断线重连机制
This commit is contained in:
@@ -27,20 +27,21 @@ namespace DOAN.ServiceCore
|
|||||||
notificationHubContext = hubContext;
|
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 = 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);
|
_executingTask = ExecuteAsync(_cancellationTokenSource.Token);
|
||||||
|
|
||||||
// 设置定时器每10分钟刷新一次料架层和点位表 .FromMinutes(1)
|
// 设置定时器每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)
|
public async Task StopAsync(CancellationToken cancellationToken)
|
||||||
@@ -78,21 +79,38 @@ namespace DOAN.ServiceCore
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task ExecuteAsync(CancellationToken stoppingToken)
|
private async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"PCL定时任务ExecuteAsync!");
|
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PCL定时任务ExecuteAsync!");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (!stoppingToken.IsCancellationRequested)
|
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;
|
byte[] plcSensorValues;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 读取1开头100,101。。。等全部地址数据
|
// 读取1开头100,101。。。等全部地址数据
|
||||||
plcSensorValues = pLCTool.ReadAllValue("VB100", 13);
|
plcSensorValues = pLCTool.ReadAllValue("VB100", 13);
|
||||||
|
if (plcSensorValues != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 成功读取PLC数据,字节数: {plcSensorValues.Length}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,12 +352,12 @@ namespace DOAN.ServiceCore
|
|||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
Console.WriteLine("任务已取消");
|
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 任务已取消");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine(
|
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
|
|||||||
/// 刷新料架层和点位表数据
|
/// 刷新料架层和点位表数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state"></param>
|
/// <param name="state"></param>
|
||||||
private async void RefreshData(object state)
|
private async Task RefreshData(object state)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Console.WriteLine($"刷新料架点位和按钮");
|
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 开始刷新料架点位和按钮数据");
|
||||||
storagelocationList = await Context.Queryable<Storagelocation>().ToListAsync();
|
// 检查PLC连接状态,如果断开则尝试重连
|
||||||
pointPositionList = await Context.Queryable<PlcAddressTable>().ToListAsync();
|
if (!pLCTool.IsConnected())
|
||||||
buttonPositionList = await Context.Queryable<PlcButton>().ToListAsync();
|
{
|
||||||
|
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<Storagelocation>().ToListAsync();
|
||||||
|
var newPointPositionList = await Context.Queryable<PlcAddressTable>().ToListAsync();
|
||||||
|
var newButtonPositionList = await Context.Queryable<PlcButton>().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)
|
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}");
|
||||||
// 如果刷新失败,保持现有数据不变
|
// 如果刷新失败,保持现有数据不变
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用于Timer回调的适配器方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state"></param>
|
||||||
|
private void RefreshDataTimerCallback(object state)
|
||||||
|
{
|
||||||
|
_ = RefreshData(state); // 不等待异步操作完成,避免阻塞Timer
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新料架位置的箱子数量并记录库存日志
|
/// 更新料架位置的箱子数量并记录库存日志
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -404,16 +453,24 @@ namespace DOAN.ServiceCore
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// 取消令牌源
|
||||||
_cancellationTokenSource.Cancel();
|
_cancellationTokenSource.Cancel();
|
||||||
_executingTask?.GetAwaiter().GetResult(); // 确保任务完成
|
|
||||||
|
// 等待任务完成,但设置超时以避免死锁
|
||||||
|
if (_executingTask != null)
|
||||||
|
{
|
||||||
|
Task.WaitAny(_executingTask, Task.Delay(5000)); // 最多等待5秒
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放资源
|
||||||
_cancellationTokenSource.Dispose();
|
_cancellationTokenSource.Dispose();
|
||||||
refreshTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
refreshTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
||||||
refreshTimer?.Dispose();
|
refreshTimer?.Dispose();
|
||||||
pLCTool.ConnectClose();
|
pLCTool?.ConnectClose();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine("DoanBackGround线程Dispose异常:" + ex.Message);
|
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] DoanBackGround线程Dispose异常: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ namespace DOAN.Infrastructure.PLC
|
|||||||
// 私有连接对象
|
// 私有连接对象
|
||||||
private SiemensS7Net siemensTcpNet = null;
|
private SiemensS7Net siemensTcpNet = null;
|
||||||
private readonly string plcAddress;
|
private readonly string plcAddress;
|
||||||
|
private const int ReconnectDelayMs = 5000;
|
||||||
|
|
||||||
public PLCTool()
|
public PLCTool()
|
||||||
{
|
{
|
||||||
plcAddress = AppSettings.GetConfig("PLCConfig:Address");
|
plcAddress = AppSettings.GetConfig("PLCConfig:Address");
|
||||||
@@ -34,21 +36,77 @@ namespace DOAN.Infrastructure.PLC
|
|||||||
var connect = siemensTcpNet.ConnectServer();
|
var connect = siemensTcpNet.ConnectServer();
|
||||||
if (connect.IsSuccess)
|
if (connect.IsSuccess)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"PLC连接成功,地址{plcAddress}");
|
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC连接成功,地址{plcAddress}");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($"PLC连接失败,地址{plcAddress}: {connect.Message}");
|
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] PLC连接失败,地址{plcAddress}: {connect.Message}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查PLC连接状态
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>连接正常返回true,否则返回false</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重连PLC
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>重连成功返回true,失败返回false</returns>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 向PLC写入单个bit值
|
/// 向PLC写入单个bit值
|
||||||
@@ -87,7 +145,7 @@ namespace DOAN.Infrastructure.PLC
|
|||||||
}
|
}
|
||||||
else
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user