init commit
This commit is contained in:
@@ -0,0 +1,412 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user