945 lines
25 KiB
Markdown
945 lines
25 KiB
Markdown
# DoorController 开发手册
|
||
|
||
## 目录
|
||
- [概述](#概述)
|
||
- [基础架构](#基础架构)
|
||
- [开发步骤](#开发步骤)
|
||
- [实现示例](#实现示例)
|
||
- [特性说明](#特性说明)
|
||
- [线程安全](#线程安全)
|
||
- [最佳实践](#最佳实践)
|
||
- [注意事项](#注意事项)
|
||
- [附录](#附录)
|
||
|
||
---
|
||
|
||
## 概述
|
||
|
||
`DoorController` 是门控制系统的核心组件,采用抽象基类设计,支持扩展不同类型的门控制器实现。本手册指导开发者如何创建自定义的门控制器。
|
||
|
||
### 核心概念
|
||
|
||
- **BasicDoorController**:门控制器抽象基类,定义通用接口和属性
|
||
- **DoorTypeAttribute**:类型特性,用于标记控制器类型,支持动态实例化
|
||
- **DoorState**:门状态枚举(Closed/Open/Unknown)
|
||
- **DoorControllerState**:控制器状态枚举(Offline/Online/Connecting/Error)
|
||
|
||
### 设计原则
|
||
|
||
1. **抽象化**:所有通信细节封装在具体实现类中
|
||
2. **线程安全**:状态读取和控制写入分离,通信操作在内部线程完成
|
||
3. **可扩展性**:通过 `DoorTypeAttribute` 实现类型自动识别和动态加载
|
||
4. **热更新支持**:配置变更无需重启任务
|
||
|
||
---
|
||
|
||
## 基础架构
|
||
|
||
### 1. 类继承关系
|
||
|
||
```
|
||
BasicDoorController (抽象基类)
|
||
│
|
||
└── ModbusDoorController (Modbus TCP 实现)
|
||
└── [其他实现...]
|
||
```
|
||
|
||
### 2. BasicDoorController 核心属性
|
||
|
||
| 属性 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `Index` | int | 控制器索引(唯一标识) |
|
||
| `Ip` | string | IP地址 |
|
||
| `Port` | int | 端口号 |
|
||
| `State` | DoorControllerState | 控制器状态 |
|
||
| `IsOnline` | bool | 是否在线(只读) |
|
||
| `DoorStates` | Dictionary<int, DoorState> | 门状态字典 |
|
||
| `DoorControlTargets` | Dictionary<int, bool> | 门目标控制状态字典 |
|
||
| `DoorConfigs` | Dictionary<int, DoorModel> | 门配置信息字典 |
|
||
| `LastUpdateTime` | DateTime | 最后更新时间 |
|
||
| `ErrorMessage` | string | 错误信息 |
|
||
|
||
### 3. 核心方法
|
||
|
||
#### 3.1 必须实现的方法(抽象方法)
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 读取门状态(开到位信号)
|
||
/// </summary>
|
||
/// <param name="doorIndex">门索引</param>
|
||
/// <returns>true=打开,false=关闭</returns>
|
||
public abstract bool ReadDoorState(int doorIndex);
|
||
|
||
/// <summary>
|
||
/// 写入门控制信号(开关控制)
|
||
/// </summary>
|
||
/// <param name="doorIndex">门索引</param>
|
||
/// <param name="open">true=打开,false=关闭</param>
|
||
public abstract void WriteDoorControl(int doorIndex, bool open);
|
||
```
|
||
|
||
#### 3.2 可重写的方法(虚方法)
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 连接门控制器
|
||
/// </summary>
|
||
public virtual void Connect() { }
|
||
|
||
/// <summary>
|
||
/// 断开连接
|
||
/// </summary>
|
||
public virtual void Disconnect() { }
|
||
|
||
/// <summary>
|
||
/// 更新控制器状态
|
||
/// </summary>
|
||
public virtual void UpdateState(DoorControllerState newState, string errorMessage = "") { }
|
||
|
||
/// <summary>
|
||
/// 设置门的目标控制状态
|
||
/// </summary>
|
||
public virtual void SetDoorControlTarget(int doorIndex, bool open) { }
|
||
```
|
||
|
||
---
|
||
|
||
## 开发步骤
|
||
|
||
### 步骤1:创建控制器类
|
||
|
||
创建新类并继承 `BasicDoorController`:
|
||
|
||
```csharp
|
||
using StandardScene.ExtendDevice.Door;
|
||
|
||
namespace StandardScene.ExtendDevice.Door
|
||
{
|
||
public class MyDoorController : BasicDoorController
|
||
{
|
||
// 实现抽象方法
|
||
}
|
||
}
|
||
```
|
||
|
||
### 步骤2:添加 DoorTypeAttribute
|
||
|
||
使用 `DoorTypeAttribute` 标记控制器类型:
|
||
|
||
```csharp
|
||
[DoorType("MyDoorController")]
|
||
public class MyDoorController : BasicDoorController
|
||
{
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**注意**:`DoorTypeAttribute` 的 `Name` 参数将显示在配置界面的类型下拉框中,并用于配置文件中的类型标识。
|
||
|
||
### 步骤3:实现抽象方法
|
||
|
||
实现 `ReadDoorState` 和 `WriteDoorControl` 方法:
|
||
|
||
```csharp
|
||
public override bool ReadDoorState(int doorIndex)
|
||
{
|
||
// 读取门状态逻辑
|
||
// 返回 true=打开,false=关闭
|
||
}
|
||
|
||
public override void WriteDoorControl(int doorIndex, bool open)
|
||
{
|
||
// 写入门控制信号逻辑
|
||
}
|
||
```
|
||
|
||
### 步骤4:重写 Connect/Disconnect 方法
|
||
|
||
如果需要初始化连接、启动后台任务等,重写 `Connect` 和 `Disconnect` 方法:
|
||
|
||
```csharp
|
||
public override void Connect()
|
||
{
|
||
base.Connect(); // 调用基类方法更新状态
|
||
|
||
// 初始化连接
|
||
// 启动后台任务
|
||
// 读取初始状态
|
||
}
|
||
|
||
public override void Disconnect()
|
||
{
|
||
// 停止后台任务
|
||
// 关闭连接
|
||
|
||
base.Disconnect(); // 调用基类方法更新状态
|
||
}
|
||
```
|
||
|
||
### 步骤5:实现线程安全的控制逻辑
|
||
|
||
如果需要定时读取状态或根据 `DoorControlTargets` 下发控制指令,实现后台任务:
|
||
|
||
```csharp
|
||
private CancellationTokenSource _cancellationTokenSource;
|
||
private Task _readTask;
|
||
|
||
public override void Connect()
|
||
{
|
||
base.Connect();
|
||
|
||
// 启动定时读取任务
|
||
_cancellationTokenSource = new CancellationTokenSource();
|
||
_readTask = Task.Run(() => ReadDoorStatesLoop(_cancellationTokenSource.Token));
|
||
}
|
||
|
||
private void ReadDoorStatesLoop(CancellationToken cancellationToken)
|
||
{
|
||
while (!cancellationToken.IsCancellationRequested)
|
||
{
|
||
// 读取所有门的状态
|
||
ReadAllDoorStates();
|
||
|
||
// 根据 DoorControlTargets 下发控制指令
|
||
ApplyDoorControlTargets();
|
||
|
||
Thread.Sleep(ReadInterval);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 实现示例
|
||
|
||
### 示例1:ModbusDoorController(完整实现)
|
||
|
||
```csharp
|
||
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
|
||
{
|
||
private ModbusRtu _modbusClient;
|
||
private readonly object _syncLock = new object();
|
||
private bool _isStarted = false;
|
||
private readonly Dictionary<int, bool> _lastSentControl = new Dictionary<int, bool>();
|
||
private CancellationTokenSource _cancellationTokenSource;
|
||
private Task _readTask;
|
||
|
||
// 可配置参数
|
||
public int ReadInterval { get; set; } = 1000; // 读取间隔(毫秒)
|
||
public int ReconnectInterval { get; set; } = 3000; // 重连间隔(毫秒)
|
||
public byte SlaveAddress { get; set; } = 1; // Modbus 从站地址
|
||
|
||
/// <summary>
|
||
/// 线程安全的设置门控制目标
|
||
/// </summary>
|
||
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 ex)
|
||
{
|
||
UpdateState(DoorControllerState.Connecting);
|
||
Diagnosis.Log($"ModbusDoorController[{Index}] 初次连接失败: {ex.Message}", "ModbusDoorController", true);
|
||
}
|
||
|
||
// 启动定时读取任务
|
||
_cancellationTokenSource = new CancellationTokenSource();
|
||
_readTask = Task.Run(() => ReadDoorStatesLoop(_cancellationTokenSource.Token));
|
||
|
||
_isStarted = true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
UpdateState(DoorControllerState.Error, $"初始化失败: {ex.Message}");
|
||
_isStarted = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 断开连接
|
||
/// </summary>
|
||
public override void Disconnect()
|
||
{
|
||
lock (_syncLock)
|
||
{
|
||
if (!_isStarted) return;
|
||
|
||
try
|
||
{
|
||
_cancellationTokenSource?.Cancel();
|
||
_readTask?.Wait(1000);
|
||
|
||
_modbusClient?.Close();
|
||
_modbusClient = null;
|
||
|
||
_isStarted = false;
|
||
UpdateState(DoorControllerState.Offline);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
UpdateState(DoorControllerState.Error, $"断开连接失败: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <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);
|
||
TryReconnect();
|
||
isConnected = _modbusClient?.modbusRtu?.Connected ?? false;
|
||
if (!isConnected)
|
||
{
|
||
Thread.Sleep(ReadInterval);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// 读取所有门的状态
|
||
ReadAllDoorStates();
|
||
|
||
// 根据目标控制状态下发控制指令
|
||
ApplyDoorControlTargets();
|
||
|
||
UpdateState(DoorControllerState.Online);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Diagnosis.Log($"ModbusDoorController[{Index}] 读取状态失败: {ex.Message}", "ModbusDoorController", true);
|
||
UpdateState(DoorControllerState.Error, $"读取状态失败: {ex.Message}");
|
||
TryReconnect();
|
||
}
|
||
|
||
Thread.Sleep(ReadInterval);
|
||
}
|
||
}
|
||
|
||
/// <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}状态失败: {ex.Message}", "ModbusDoorController", true);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据 DoorControlTargets 下发控制指令
|
||
/// </summary>
|
||
private void ApplyDoorControlTargets()
|
||
{
|
||
lock (_syncLock)
|
||
{
|
||
foreach (var doorConfig in DoorConfigs.Values)
|
||
{
|
||
var doorIndex = doorConfig.Index;
|
||
|
||
// 获取目标控制状态
|
||
bool target = false;
|
||
DoorControlTargets.TryGetValue(doorIndex, out target);
|
||
|
||
// 获取上一次已下发的状态
|
||
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}控制指令失败: {ex.Message}", "ModbusDoorController", true);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取门状态(开到位信号)
|
||
/// </summary>
|
||
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>
|
||
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, new[] { open });
|
||
}
|
||
}
|
||
|
||
private void TryReconnect()
|
||
{
|
||
// 重连逻辑...
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 示例2:简单门控制器(最小实现)
|
||
|
||
如果不需要定时读取或后台任务,可以实现最简单的版本:
|
||
|
||
```csharp
|
||
using System;
|
||
using StandardScene.ExtendDevice.Door;
|
||
|
||
namespace StandardScene.ExtendDevice.Door
|
||
{
|
||
/// <summary>
|
||
/// 简单门控制器实现(同步模式)
|
||
/// </summary>
|
||
[DoorType("SimpleDoorController")]
|
||
public class SimpleDoorController : BasicDoorController
|
||
{
|
||
private SimpleDoorClient _client;
|
||
|
||
public override void Connect()
|
||
{
|
||
base.Connect();
|
||
_client = new SimpleDoorClient(Ip, Port);
|
||
UpdateState(DoorControllerState.Online);
|
||
}
|
||
|
||
public override void Disconnect()
|
||
{
|
||
_client?.Close();
|
||
_client = null;
|
||
base.Disconnect();
|
||
}
|
||
|
||
public override bool ReadDoorState(int doorIndex)
|
||
{
|
||
if (!DoorConfigs.TryGetValue(doorIndex, out var doorConfig))
|
||
{
|
||
throw new ArgumentException($"门{doorIndex}不存在");
|
||
}
|
||
|
||
if (_client == null || !_client.IsConnected)
|
||
{
|
||
throw new InvalidOperationException("连接未建立");
|
||
}
|
||
|
||
// 读取门状态
|
||
return _client.ReadDoorStatus(doorConfig.OpenStatusAddress);
|
||
}
|
||
|
||
public override void WriteDoorControl(int doorIndex, bool open)
|
||
{
|
||
if (!DoorConfigs.TryGetValue(doorIndex, out var doorConfig))
|
||
{
|
||
throw new ArgumentException($"门{doorIndex}不存在");
|
||
}
|
||
|
||
if (_client == null || !_client.IsConnected)
|
||
{
|
||
throw new InvalidOperationException("连接未建立");
|
||
}
|
||
|
||
// 写入控制信号
|
||
_client.WriteDoorControl(doorConfig.ControlAddress, open);
|
||
|
||
// 同步更新门状态(可选)
|
||
var state = _client.ReadDoorStatus(doorConfig.OpenStatusAddress);
|
||
UpdateDoorState(doorIndex, state ? DoorState.Open : DoorState.Closed);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重写 SetDoorControlTarget 以立即执行控制
|
||
/// </summary>
|
||
public override void SetDoorControlTarget(int doorIndex, bool open)
|
||
{
|
||
base.SetDoorControlTarget(doorIndex, open);
|
||
|
||
// 立即执行控制(同步模式)
|
||
try
|
||
{
|
||
WriteDoorControl(doorIndex, open);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Diagnosis.Log($"SimpleDoorController[{Index}] 控制门{doorIndex}失败: {ex.Message}", "SimpleDoorController", true);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 特性说明
|
||
|
||
### DoorTypeAttribute
|
||
|
||
`DoorTypeAttribute` 用于标记门控制器类型,支持动态实例化。
|
||
|
||
**定义**:
|
||
```csharp
|
||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||
public class DoorTypeAttribute : Attribute
|
||
{
|
||
public string Name { get; }
|
||
|
||
public DoorTypeAttribute(string name)
|
||
{
|
||
Name = name ?? throw new ArgumentNullException(nameof(name));
|
||
}
|
||
}
|
||
```
|
||
|
||
**使用**:
|
||
```csharp
|
||
[DoorType("MyDoorController")]
|
||
public class MyDoorController : BasicDoorController
|
||
{
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**作用**:
|
||
1. **类型标识**:`Name` 参数作为类型的唯一标识,用于配置文件中指定类型
|
||
2. **UI显示**:配置界面会自动识别并显示所有带此特性的控制器类型
|
||
3. **动态实例化**:`DoorMission` 根据 `Name` 动态查找并创建实例
|
||
|
||
**注意**:
|
||
- `Name` 必须唯一
|
||
- 建议使用类名作为 `Name`
|
||
- `Name` 会显示在配置界面的类型下拉框中
|
||
|
||
---
|
||
|
||
## 线程安全
|
||
|
||
### 1. 设计原则
|
||
|
||
门控制器采用**读写分离**的线程安全设计:
|
||
- **读取**:`DoorMission` 通过 `controller.DoorStates` 直接访问门状态(受锁保护)
|
||
- **写入**:`DoorMission` 通过 `controller.SetDoorControlTarget()` 设置目标状态,实际通信由门控制器内部线程完成
|
||
|
||
### 2. 线程安全要求
|
||
|
||
#### 2.1 SetDoorControlTarget 方法
|
||
|
||
如果多个线程可能同时调用 `SetDoorControlTarget`,必须加锁保护:
|
||
|
||
```csharp
|
||
private readonly object _syncLock = new object();
|
||
|
||
public override void SetDoorControlTarget(int doorIndex, bool open)
|
||
{
|
||
lock (_syncLock)
|
||
{
|
||
base.SetDoorControlTarget(doorIndex, open);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2.2 ReadDoorState 和 WriteDoorControl 方法
|
||
|
||
如果这些方法会被多个线程调用,必须加锁保护:
|
||
|
||
```csharp
|
||
public override bool ReadDoorState(int doorIndex)
|
||
{
|
||
lock (_syncLock)
|
||
{
|
||
// 读取逻辑
|
||
}
|
||
}
|
||
|
||
public override void WriteDoorControl(int doorIndex, bool open)
|
||
{
|
||
lock (_syncLock)
|
||
{
|
||
// 写入逻辑
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2.3 后台任务访问共享资源
|
||
|
||
如果后台任务会访问 `DoorStates`、`DoorControlTargets` 等共享资源,必须加锁:
|
||
|
||
```csharp
|
||
private void ReadAllDoorStates()
|
||
{
|
||
lock (_syncLock)
|
||
{
|
||
foreach (var doorConfig in DoorConfigs.Values)
|
||
{
|
||
var state = ReadDoorState(doorConfig.Index);
|
||
UpdateDoorState(doorConfig.Index, state ? DoorState.Open : DoorState.Closed);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void ApplyDoorControlTargets()
|
||
{
|
||
lock (_syncLock)
|
||
{
|
||
foreach (var doorConfig in DoorConfigs.Values)
|
||
{
|
||
// 访问 DoorControlTargets
|
||
// 调用 WriteDoorControl
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. 推荐实现模式
|
||
|
||
推荐使用单一锁对象保护所有共享资源:
|
||
|
||
```csharp
|
||
public class MyDoorController : BasicDoorController
|
||
{
|
||
private readonly object _syncLock = new object();
|
||
|
||
// 所有访问共享资源的方法都使用同一个锁
|
||
public override void SetDoorControlTarget(int doorIndex, bool open)
|
||
{
|
||
lock (_syncLock) { /* ... */ }
|
||
}
|
||
|
||
public override bool ReadDoorState(int doorIndex)
|
||
{
|
||
lock (_syncLock) { /* ... */ }
|
||
}
|
||
|
||
public override void WriteDoorControl(int doorIndex, bool open)
|
||
{
|
||
lock (_syncLock) { /* ... */ }
|
||
}
|
||
|
||
private void ReadAllDoorStates()
|
||
{
|
||
lock (_syncLock) { /* ... */ }
|
||
}
|
||
|
||
private void ApplyDoorControlTargets()
|
||
{
|
||
lock (_syncLock) { /* ... */ }
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 最佳实践
|
||
|
||
### 1. 错误处理
|
||
|
||
- **连接错误**:设置状态为 `Connecting` 或 `Error`,并记录错误信息
|
||
- **读取错误**:记录日志,但不抛出异常,返回默认值或保持当前状态
|
||
- **写入错误**:记录日志,尝试重连,但不影响其他门的操作
|
||
|
||
**示例**:
|
||
```csharp
|
||
public override bool ReadDoorState(int doorIndex)
|
||
{
|
||
try
|
||
{
|
||
// 读取逻辑
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Diagnosis.Log($"读取门{doorIndex}状态失败: {ex.Message}", "MyDoorController", true);
|
||
return false; // 返回默认值
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 状态管理
|
||
|
||
- **及时更新状态**:在连接、断开、错误时及时调用 `UpdateState()`
|
||
- **更新最后更新时间**:在状态变化时更新 `LastUpdateTime`
|
||
- **错误信息**:在错误时记录详细的错误信息到 `ErrorMessage`
|
||
|
||
**示例**:
|
||
```csharp
|
||
try
|
||
{
|
||
_client.Connect();
|
||
UpdateState(DoorControllerState.Online);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
UpdateState(DoorControllerState.Error, $"连接失败: {ex.Message}");
|
||
}
|
||
```
|
||
|
||
### 3. 资源释放
|
||
|
||
- **实现 Disconnect**:确保正确关闭连接和释放资源
|
||
- **实现析构函数**:作为最后的安全网,确保资源释放
|
||
|
||
**示例**:
|
||
```csharp
|
||
public override void Disconnect()
|
||
{
|
||
try
|
||
{
|
||
_cancellationTokenSource?.Cancel();
|
||
_readTask?.Wait(1000);
|
||
_client?.Close();
|
||
_client = null;
|
||
UpdateState(DoorControllerState.Offline);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
UpdateState(DoorControllerState.Error, $"断开连接失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
~MyDoorController()
|
||
{
|
||
Disconnect();
|
||
}
|
||
```
|
||
|
||
### 4. 配置验证
|
||
|
||
在 `Connect()` 中验证配置的完整性:
|
||
|
||
```csharp
|
||
public override void Connect()
|
||
{
|
||
if (string.IsNullOrWhiteSpace(Ip))
|
||
{
|
||
UpdateState(DoorControllerState.Error, "IP地址未配置");
|
||
return;
|
||
}
|
||
|
||
if (Port <= 0 || Port > 65535)
|
||
{
|
||
UpdateState(DoorControllerState.Error, "端口号无效");
|
||
return;
|
||
}
|
||
|
||
if (DoorConfigs.Count == 0)
|
||
{
|
||
UpdateState(DoorControllerState.Error, "未配置门");
|
||
return;
|
||
}
|
||
|
||
// 连接逻辑...
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 注意事项
|
||
|
||
### 1. DoorTypeAttribute 命名
|
||
|
||
- `Name` 必须与配置文件中使用的类型名称一致
|
||
- 建议使用类名作为 `Name`
|
||
- 避免使用特殊字符
|
||
|
||
### 2. 异常处理
|
||
|
||
- **不要抛出未处理的异常**:所有异常都应该被捕获并记录
|
||
- **不要阻塞线程**:长时间操作应该在后台线程中执行
|
||
- **提供错误信息**:通过 `ErrorMessage` 属性提供详细的错误信息
|
||
|
||
### 3. 性能考虑
|
||
|
||
- **避免频繁的连接/断开**:保持连接持久化
|
||
- **批量读取**:如果可能,批量读取多个门的状态
|
||
- **控制读取频率**:根据实际需求设置合理的读取间隔
|
||
|
||
### 4. 兼容性
|
||
|
||
- **向后兼容**:新版本应该兼容旧版本的配置格式
|
||
- **版本标识**:如果需要,可以在实现中添加版本检查
|
||
|
||
---
|
||
|
||
## 附录
|
||
|
||
### A. DoorModel 说明
|
||
|
||
```csharp
|
||
public class DoorModel
|
||
{
|
||
/// <summary>
|
||
/// 门索引
|
||
/// </summary>
|
||
public int Index { get; set; }
|
||
|
||
/// <summary>
|
||
/// 开关控制信号地址
|
||
/// </summary>
|
||
public ushort ControlAddress { get; set; }
|
||
|
||
/// <summary>
|
||
/// 开到位信号地址
|
||
/// </summary>
|
||
public ushort OpenStatusAddress { get; set; }
|
||
}
|
||
```
|
||
|
||
### B. DoorState 枚举
|
||
|
||
```csharp
|
||
public enum DoorState
|
||
{
|
||
Closed = 0, // 关闭
|
||
Open = 1, // 打开
|
||
Unknown = 2 // 未知状态
|
||
}
|
||
```
|
||
|
||
### C. DoorControllerState 枚举
|
||
|
||
```csharp
|
||
public enum DoorControllerState
|
||
{
|
||
Offline = 0, // 离线
|
||
Online = 1, // 在线
|
||
Connecting = 2, // 连接中
|
||
Error = 3 // 错误
|
||
}
|
||
```
|
||
|
||
### D. 开发检查清单
|
||
|
||
- [ ] 继承 `BasicDoorController`
|
||
- [ ] 添加 `DoorTypeAttribute` 特性
|
||
- [ ] 实现 `ReadDoorState` 方法
|
||
- [ ] 实现 `WriteDoorControl` 方法
|
||
- [ ] 重写 `Connect` 方法(如需要)
|
||
- [ ] 重写 `Disconnect` 方法(如需要)
|
||
- [ ] 实现线程安全(如需要)
|
||
- [ ] 添加错误处理
|
||
- [ ] 添加资源释放逻辑
|
||
- [ ] 添加诊断日志
|
||
- [ ] 测试连接/断开
|
||
- [ ] 测试读取状态
|
||
- [ ] 测试控制写入
|
||
- [ ] 测试配置热更新
|
||
|
||
---
|
||
|
||
**文档更新时间**:2025-01-09
|