Files
StandardSence/StandardScene.Devices/Door/ModbusDoorController.cs
T
2026-06-14 11:19:15 +08:00

413 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using StandardScene.Utils;
using SimpleCore.Library;
namespace StandardScene.ExtendDevice.Door
{
/// <summary>
/// Modbus 门控制器实现
/// </summary>
[DoorType("ModbusDoorController")]
public class ModbusDoorController : BasicDoorController
{
/// <summary>
/// Modbus TCP 客户端
/// </summary>
private ModbusRtu _modbusClient;
/// <summary>
/// 同步锁
/// </summary>
private readonly object _syncLock = new object();
/// <summary>
/// 是否已启动
/// </summary>
private bool _isStarted = false;
/// <summary>
/// 最近一次已下发的门控制状态,键为门索引
/// </summary>
private readonly Dictionary<int, bool> _lastSentControl = new Dictionary<int, bool>();
/// <summary>
/// 定时读取任务取消令牌
/// </summary>
private CancellationTokenSource _cancellationTokenSource;
/// <summary>
/// 定时读取任务
/// </summary>
private Task _readTask;
/// <summary>
/// 读取间隔(毫秒),默认1000ms
/// </summary>
public int ReadInterval { get; set; } = 1000;
/// <summary>
/// 重连间隔(毫秒),默认3000ms
/// </summary>
public int ReconnectInterval { get; set; } = 3000;
/// <summary>
/// 上次重连尝试时间
/// </summary>
private DateTime _lastReconnectAttempt = DateTime.MinValue;
/// <summary>
/// Modbus 从站地址,默认1
/// </summary>
public byte SlaveAddress { get; set; } = 1;
/// <summary>
/// 设置门的目标控制状态(线程安全实现)
/// 仅修改内存字段,不直接进行通信,实际通信在内部线程中完成
/// </summary>
/// <param name="doorIndex">门索引</param>
/// <param name="open">true=打开,false=关闭</param>
public override void SetDoorControlTarget(int doorIndex, bool open)
{
lock (_syncLock)
{
base.SetDoorControlTarget(doorIndex, open);
}
}
/// <summary>
/// 连接门控制器
/// </summary>
public override void Connect()
{
lock (_syncLock)
{
if (_isStarted)
{
return;
}
try
{
UpdateState(DoorControllerState.Connecting);
// 根据配置的门初始化门状态
var doorIndices = DoorConfigs.Keys.OrderBy(k => k).ToList();
InitializeDoors(doorIndices);
// 初始化最近一次已下发的控制状态
_lastSentControl.Clear();
foreach (var index in doorIndices)
{
_lastSentControl[index] = false;
if (!DoorControlTargets.ContainsKey(index))
{
DoorControlTargets[index] = false;
}
}
// 尝试连接 Modbus TCP 客户端
try
{
_modbusClient = new ModbusRtu();
_modbusClient.StartTcpRtu(Ip, Port);
UpdateState(DoorControllerState.Online);
}
catch (Exception connectEx)
{
UpdateState(DoorControllerState.Connecting);
Diagnosis.Log($"ModbusDoorController[{Index}] 初次连接失败,将在后台持续重连: {ExceptionFormatter.FormatEx(connectEx)}", "ModbusDoorController", true);
}
// 启动定时读取任务
_cancellationTokenSource = new CancellationTokenSource();
_readTask = Task.Run(() => ReadDoorStatesLoop(_cancellationTokenSource.Token));
_isStarted = true;
}
catch (Exception ex)
{
UpdateState(DoorControllerState.Error, $"初始化失败: {ex.Message}");
_isStarted = false;
Diagnosis.Log($"ModbusDoorController[{Index}] 初始化失败: {ExceptionFormatter.FormatEx(ex)}", "ModbusDoorController", true);
}
}
}
/// <summary>
/// 断开连接
/// </summary>
public override void Disconnect()
{
lock (_syncLock)
{
if (!_isStarted)
{
return;
}
try
{
// 停止读取任务
_cancellationTokenSource?.Cancel();
_readTask?.Wait(1000);
// 关闭 Modbus 连接
_modbusClient?.Close();
_modbusClient = null;
_isStarted = false;
UpdateState(DoorControllerState.Offline);
}
catch (Exception ex)
{
UpdateState(DoorControllerState.Error, $"断开连接失败: {ex.Message}");
Diagnosis.Log($"ModbusDoorController[{Index}] 断开连接失败: {ExceptionFormatter.FormatEx(ex)}", "ModbusDoorController", true);
}
}
}
/// <summary>
/// 定时读取门状态循环
/// </summary>
private void ReadDoorStatesLoop(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
if (!_isStarted)
{
break;
}
// 检查连接状态
bool isConnected = _modbusClient?.modbusRtu?.Connected ?? false;
if (_modbusClient == null || !isConnected)
{
UpdateState(DoorControllerState.Connecting);
// 控制重连频率
var timeSinceLastReconnect = (DateTime.Now - _lastReconnectAttempt).TotalMilliseconds;
if (timeSinceLastReconnect >= ReconnectInterval)
{
_lastReconnectAttempt = DateTime.Now;
TryReconnect();
}
isConnected = _modbusClient?.modbusRtu?.Connected ?? false;
if (_modbusClient == null || !isConnected)
{
Thread.Sleep(ReadInterval);
continue;
}
}
// 读取所有门的状态
ReadAllDoorStates();
// 根据目标控制状态下发控制指令
ApplyDoorControlTargets();
// 更新在线状态
UpdateState(DoorControllerState.Online);
}
catch (Exception ex)
{
Diagnosis.Log($"ModbusDoorController[{Index}] 读取状态失败: {ExceptionFormatter.FormatEx(ex)}", "ModbusDoorController", true);
UpdateState(DoorControllerState.Error, $"读取状态失败: {ex.Message}");
// 如果连接失败,尝试重连
TryReconnect();
}
// 等待指定间隔
Thread.Sleep(ReadInterval);
}
}
/// <summary>
/// 尝试重连 Modbus TCP 连接
/// </summary>
private void TryReconnect()
{
ModbusRtu oldClient = null;
try
{
// 安全关闭旧连接
if (_modbusClient != null)
{
oldClient = _modbusClient;
_modbusClient = null;
try
{
oldClient.Close();
}
catch
{
// 忽略关闭时的异常
}
finally
{
oldClient = null;
}
}
// 创建新连接
_modbusClient = new ModbusRtu();
_modbusClient.StartTcpRtu(Ip, Port);
Diagnosis.Log($"ModbusDoorController[{Index}] 重连成功", "ModbusDoorController", false);
}
catch (Exception ex)
{
// 重连失败,确保资源释放
if (_modbusClient != null)
{
try
{
_modbusClient.Close();
}
catch
{
// 忽略关闭异常
}
_modbusClient = null;
}
Diagnosis.Log($"ModbusDoorController[{Index}] 重连失败: {ExceptionFormatter.FormatEx(ex)}", "ModbusDoorController", false);
}
}
/// <summary>
/// 读取所有门的状态
/// </summary>
private void ReadAllDoorStates()
{
lock (_syncLock)
{
foreach (var doorConfig in DoorConfigs.Values)
{
try
{
var state = ReadDoorState(doorConfig.Index);
UpdateDoorState(doorConfig.Index, state ? DoorState.Open : DoorState.Closed);
}
catch (Exception ex)
{
Diagnosis.Log($"ModbusDoorController[{Index}] 读取门{doorConfig.Index}状态失败: {ExceptionFormatter.FormatEx(ex)}", "ModbusDoorController", true);
}
}
}
}
/// <summary>
/// 根据 DoorControlTargets 中的目标状态,下发门控制指令
/// </summary>
private void ApplyDoorControlTargets()
{
lock (_syncLock)
{
foreach (var doorConfig in DoorConfigs.Values)
{
var doorIndex = doorConfig.Index;
// 获取目标控制状态,默认false
bool target = false;
DoorControlTargets.TryGetValue(doorIndex, out target);
// 如果门配置为不允许发送任何控制指令,则跳过(既不打开也不关闭)
if (doorConfig.NoControl)
{
continue;
}
// 获取上一次已下发的状态
bool last;
var hasLast = _lastSentControl.TryGetValue(doorIndex, out last);
// 如果没有记录或状态发生变化,则下发控制
if (!hasLast || last != target)
{
try
{
WriteDoorControl(doorIndex, target);
_lastSentControl[doorIndex] = target;
}
catch (Exception ex)
{
Diagnosis.Log($"ModbusDoorController[{Index}] 下发门{doorIndex}控制指令失败: {ExceptionFormatter.FormatEx(ex)}", "ModbusDoorController", true);
}
}
}
}
}
/// <summary>
/// 读取门状态(开到位信号)
/// </summary>
/// <param name="doorIndex">门索引</param>
/// <returns>true=打开,false=关闭</returns>
public override bool ReadDoorState(int doorIndex)
{
lock (_syncLock)
{
if (!DoorConfigs.TryGetValue(doorIndex, out var doorConfig))
{
throw new ArgumentException($"门{doorIndex}不存在");
}
if (_modbusClient == null || !_modbusClient.modbusRtu.Connected)
{
throw new InvalidOperationException("Modbus连接未建立");
}
// 读取开到位信号
var data = _modbusClient.ReadDiscreteInputs_02(SlaveAddress, doorConfig.OpenStatusAddress, 1);
return data != null && data.Length > 0 && data[0];
}
}
/// <summary>
/// 写入门控制信号(开关控制)
/// </summary>
/// <param name="doorIndex">门索引</param>
/// <param name="open">true=打开,false=关闭</param>
public override void WriteDoorControl(int doorIndex, bool open)
{
lock (_syncLock)
{
if (!DoorConfigs.TryGetValue(doorIndex, out var doorConfig))
{
throw new ArgumentException($"门{doorIndex}不存在");
}
if (_modbusClient == null || !_modbusClient.modbusRtu.Connected)
{
// 尝试重连
TryReconnect();
if (_modbusClient == null || !_modbusClient.modbusRtu.Connected)
{
throw new InvalidOperationException("Modbus连接未建立");
}
}
// 写入开关控制信号(线圈)
_modbusClient.WriteMultipleCoils_15(SlaveAddress, doorConfig.ControlAddress, [open]);
}
}
/// <summary>
/// 析构函数,确保资源释放
/// </summary>
~ModbusDoorController()
{
Disconnect();
}
}
}