192 lines
12 KiB
C#
192 lines
12 KiB
C#
using Common.Attributes;
|
||
using Model.Models;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Threading.Tasks;
|
||
using System.Threading;
|
||
using DeviceCommand.Base;
|
||
using DeviceCommand.Devices;
|
||
using System.ComponentModel;
|
||
|
||
namespace DeviceCommand.Devices
|
||
{
|
||
/// <summary>
|
||
/// 系统控制功能类型枚举 (根据 ACP硬件接线图.xlsx 三工位系统更新)
|
||
/// </summary>
|
||
public enum 功能动作 : byte
|
||
{
|
||
/// <summary>低压电源产品唤醒通断 KL30(三工位,模块1# Y1/Y3/Y5,KA2/KA4/KA6)</summary>
|
||
KL30通断,
|
||
/// <summary>低压电源产品唤醒通断 KL15(三工位,模块1# Y2/Y4/Y6,KA3/KA5/KA7)</summary>
|
||
KL15通断,
|
||
/// <summary>电子锁60Ω电阻通断(三工位,模块1# Y13/Y14/Y15)</summary>
|
||
电子锁60欧电阻,
|
||
/// <summary>温度检测1KΩ电阻通断(三工位,模块3# Y1/Y2/Y3)</summary>
|
||
温度检测1K欧电阻,
|
||
/// <summary>CC信号串1KΩ电阻(三工位,模块2# Y1/Y5/Y9)</summary>
|
||
CC信号串1K欧电阻,
|
||
/// <summary>CP信号串1KΩ电阻(三工位,模块2# Y13/Y14/Y15)</summary>
|
||
CP信号串1K欧电阻,
|
||
/// <summary>CC信号串680Ω电阻(三工位,模块2# Y2/Y6/Y10)</summary>
|
||
CC信号串680欧电阻,
|
||
/// <summary>CC信号串220Ω电阻(三工位,模块2# Y3/Y7/Y11)</summary>
|
||
CC信号串220欧电阻,
|
||
/// <summary>CC信号串100Ω电阻(三工位,模块2# Y4/Y8/Y12)</summary>
|
||
CC信号串100欧电阻,
|
||
/// <summary>蜂鸣器阈值报警(三工位,模块1# Y10/Y11/Y12)</summary>
|
||
蜂鸣器报警,
|
||
/// <summary>双向直流源(HV+)抛负载通断(仅一工位,模块1# Y7,KA8)</summary>
|
||
抛负载通断,
|
||
/// <summary>双向直流源(HV)短路测试通断(仅一工位,模块1# Y8,KA9)</summary>
|
||
短路测试HV,
|
||
/// <summary>低压源载一体机(LV)短路测试通断(仅一工位,模块1# Y9,KA10)</summary>
|
||
短路测试LV
|
||
}
|
||
|
||
/// <summary>
|
||
/// 精简后的 IO 端口信息结构体
|
||
/// </summary>
|
||
public struct IoPortInfo
|
||
{
|
||
public int ModuleIndex { get; set; } // IO板卡/模块序号 (1、2 或 3)
|
||
public ushort Address { get; set; } // 转换为 Modbus 的线圈相对偏移地址 (Y1->0, Y2->1)
|
||
|
||
public IoPortInfo(int moduleIndex, ushort address)
|
||
{
|
||
ModuleIndex = moduleIndex;
|
||
Address = address;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// IO 板卡组(三工位统一调度),封装 3 块 IO 继电器板卡的访问。
|
||
/// 根据 ACP硬件接线图.xlsx 的点位映射字典,按台架序号路由到对应板卡的线圈地址。
|
||
/// 注意:模块1# 集中承载三工位的 KL30/KL15 唤醒、蜂鸣器、电子锁及一工位专属的
|
||
/// 抛负载/短路测试;模块2# 承载三工位 CC/CP 引导信号电阻;模块3# 承载三工位温度检测电阻。
|
||
/// </summary>
|
||
[ACPCommand]
|
||
public class IOBoardGroup
|
||
{
|
||
// 声明底层的 3 个 IO 板卡实例(对应接线图中的 IO继电器模块 1#/2#/3#)
|
||
public IOBoard Board1 { get; private set; }
|
||
public IOBoard Board2 { get; private set; }
|
||
public IOBoard Board3 { get; private set; }
|
||
|
||
private readonly byte _slaveAddress = 1; // 默认 Modbus 从站号为 1
|
||
|
||
// 核心:根据 ACP硬件接线图.xlsx 严格对齐的数据字典(按工位分组,点位跨模块路由)
|
||
private static readonly Dictionary<int, Dictionary<功能动作, IoPortInfo>> IoMapping
|
||
= new Dictionary<int, Dictionary<功能动作, IoPortInfo>>()
|
||
{
|
||
// 1# 工位(一工位)
|
||
{ 1, new Dictionary<功能动作, IoPortInfo> {
|
||
{ 功能动作.KL30通断, new IoPortInfo(1, 0) }, // 模块1# Y1 -> 0 (KA2, 低压电源KL30唤醒)
|
||
{ 功能动作.KL15通断, new IoPortInfo(1, 1) }, // 模块1# Y2 -> 1 (KA3, 低压电源KL15唤醒)
|
||
{ 功能动作.抛负载通断, new IoPortInfo(1, 6) }, // 模块1# Y7 -> 6 (KA8, 双向直流源HV+抛负载)
|
||
{ 功能动作.短路测试HV, new IoPortInfo(1, 7) }, // 模块1# Y8 -> 7 (KA9, 双向直流源HV短路测试)
|
||
{ 功能动作.短路测试LV, new IoPortInfo(1, 8) }, // 模块1# Y9 -> 8 (KA10, 低压源载一体机LV短路测试)
|
||
{ 功能动作.蜂鸣器报警, new IoPortInfo(1, 9) }, // 模块1# Y10 -> 9 (1#蜂鸣器)
|
||
{ 功能动作.电子锁60欧电阻, new IoPortInfo(1, 12) }, // 模块1# Y13 -> 12 (1#电子锁60Ω)
|
||
{ 功能动作.CC信号串1K欧电阻, new IoPortInfo(2, 0) }, // 模块2# Y1 -> 0 (CC 1KΩ)
|
||
{ 功能动作.CC信号串680欧电阻, new IoPortInfo(2, 1) }, // 模块2# Y2 -> 1 (CC 680Ω)
|
||
{ 功能动作.CC信号串220欧电阻, new IoPortInfo(2, 2) }, // 模块2# Y3 -> 2 (CC 220Ω)
|
||
{ 功能动作.CC信号串100欧电阻, new IoPortInfo(2, 3) }, // 模块2# Y4 -> 3 (CC 100Ω)
|
||
{ 功能动作.CP信号串1K欧电阻, new IoPortInfo(2, 12) }, // 模块2# Y13 -> 12 (CP 1KΩ)
|
||
{ 功能动作.温度检测1K欧电阻, new IoPortInfo(3, 0) } // 模块3# Y1 -> 0 (温度检测1KΩ)
|
||
}},
|
||
// 2# 工位(二工位)
|
||
{ 2, new Dictionary<功能动作, IoPortInfo> {
|
||
{ 功能动作.KL30通断, new IoPortInfo(1, 2) }, // 模块1# Y3 -> 2 (KA4, 低压电源KL30唤醒)
|
||
{ 功能动作.KL15通断, new IoPortInfo(1, 3) }, // 模块1# Y4 -> 3 (KA5, 低压电源KL15唤醒)
|
||
{ 功能动作.蜂鸣器报警, new IoPortInfo(1, 10) }, // 模块1# Y11 -> 10 (2#蜂鸣器)
|
||
{ 功能动作.电子锁60欧电阻, new IoPortInfo(1, 13) }, // 模块1# Y14 -> 13 (2#电子锁60Ω)
|
||
{ 功能动作.CC信号串1K欧电阻, new IoPortInfo(2, 4) }, // 模块2# Y5 -> 4 (CC 1KΩ)
|
||
{ 功能动作.CC信号串680欧电阻, new IoPortInfo(2, 5) }, // 模块2# Y6 -> 5 (CC 680Ω)
|
||
{ 功能动作.CC信号串220欧电阻, new IoPortInfo(2, 6) }, // 模块2# Y7 -> 6 (CC 220Ω)
|
||
{ 功能动作.CC信号串100欧电阻, new IoPortInfo(2, 7) }, // 模块2# Y8 -> 7 (CC 100Ω)
|
||
{ 功能动作.CP信号串1K欧电阻, new IoPortInfo(2, 13) }, // 模块2# Y14 -> 13 (CP 1KΩ)
|
||
{ 功能动作.温度检测1K欧电阻, new IoPortInfo(3, 1) } // 模块3# Y2 -> 1 (温度检测1KΩ)
|
||
}},
|
||
// 3# 工位(三工位)
|
||
{ 3, new Dictionary<功能动作, IoPortInfo> {
|
||
{ 功能动作.KL30通断, new IoPortInfo(1, 4) }, // 模块1# Y5 -> 4 (KA6, 低压电源KL30唤醒)
|
||
{ 功能动作.KL15通断, new IoPortInfo(1, 5) }, // 模块1# Y6 -> 5 (KA7, 低压电源KL15唤醒)
|
||
{ 功能动作.蜂鸣器报警, new IoPortInfo(1, 11) }, // 模块1# Y12 -> 11 (3#蜂鸣器)
|
||
{ 功能动作.电子锁60欧电阻, new IoPortInfo(1, 14) }, // 模块1# Y15 -> 14 (3#电子锁60Ω)
|
||
{ 功能动作.CC信号串1K欧电阻, new IoPortInfo(2, 8) }, // 模块2# Y9 -> 8 (CC 1KΩ)
|
||
{ 功能动作.CC信号串680欧电阻, new IoPortInfo(2, 9) }, // 模块2# Y10 -> 9 (CC 680Ω)
|
||
{ 功能动作.CC信号串220欧电阻, new IoPortInfo(2, 10) }, // 模块2# Y11 -> 10 (CC 220Ω)
|
||
{ 功能动作.CC信号串100欧电阻, new IoPortInfo(2, 11) }, // 模块2# Y12 -> 11 (CC 100Ω)
|
||
{ 功能动作.CP信号串1K欧电阻, new IoPortInfo(2, 14) }, // 模块2# Y15 -> 14 (CP 1KΩ)
|
||
{ 功能动作.温度检测1K欧电阻, new IoPortInfo(3, 2) } // 模块3# Y3 -> 2 (温度检测1KΩ)
|
||
}}
|
||
};
|
||
|
||
/// <summary>
|
||
/// 构造函数,注入三个 IO 板卡实例。
|
||
/// </summary>
|
||
/// <param name="board1">IO继电器模块 1# 板卡实例</param>
|
||
/// <param name="board2">IO继电器模块 2# 板卡实例</param>
|
||
/// <param name="board3">IO继电器模块 3# 板卡实例</param>
|
||
/// <exception cref="ArgumentNullException">任一块板卡实例为空时抛出</exception>
|
||
public IOBoardGroup(IOBoard board1, IOBoard board2, IOBoard board3)
|
||
{
|
||
Board1 = board1 ?? throw new ArgumentNullException(nameof(board1), "IOBoard Board1 实例不能为空。");
|
||
Board2 = board2 ?? throw new ArgumentNullException(nameof(board2), "IOBoard Board2 实例不能为空。");
|
||
Board3 = board3 ?? throw new ArgumentNullException(nameof(board3), "IOBoard Board3 实例不能为空。");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行指定台架的功能动作控制(按接线图字典跨模块路由到对应线圈地址)
|
||
/// </summary>
|
||
/// <param name="台架序号">台架/工位序号 (1 - 3)</param>
|
||
/// <param name="动作">控制功能类型</param>
|
||
/// <param name="开关状态">开关状态 (true: 开启吸合, false: 关闭断开)</param>
|
||
/// <param name="取消令牌">异步取消令牌</param>
|
||
public async Task 执行台架动作Async(int 台架序号, 功能动作 动作, bool 开关状态, CancellationToken 取消令牌 = default)
|
||
{
|
||
// 1. 根据传入的台架序号和动作检索字典映射关系
|
||
if (!IoMapping.TryGetValue(台架序号, out var 动作字典) || !动作字典.TryGetValue(动作, out var io信息))
|
||
{
|
||
throw new ArgumentException($"未找到台架 [{台架序号}] 对应功能 [{动作}] 的 IO 点位映射配置。请注意抛负载及短路测试(HV/LV)仅存在于 1# 工位。");
|
||
}
|
||
|
||
// 2. 动态获取目标板卡实例并直接驱动开关端口
|
||
IOBoard 目标板卡 = GetBoardInstance(io信息.ModuleIndex);
|
||
await 目标板卡.写输出开关(_slaveAddress, io信息.Address, 开关状态);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 向指定板卡批量写入输出开关状态,直接透传到对应板卡的批量写线圈操作。
|
||
/// </summary>
|
||
/// <param name="板卡序号">板卡序号 (1 - 3)</param>
|
||
/// <param name="站号">Modbus 从站站号</param>
|
||
/// <param name="开始地址">线圈起始地址 (0-15)</param>
|
||
/// <param name="数据">开关状态数组 (true: 开启吸合, false: 关闭断开),例如 [true, true, false]</param>
|
||
/// <param name="取消令牌">异步取消令牌</param>
|
||
public async Task 批量写输出开关(int 板卡序号, byte 站号, ushort 开始地址, bool[] 数据, CancellationToken 取消令牌 = default)
|
||
{
|
||
await GetBoardInstance(板卡序号).批量写输出开关(站号, 开始地址, 数据);
|
||
}
|
||
/// <summary>
|
||
/// 内部路由辅助方法:根据板卡序号安全获取对应的 IO 板卡实例。
|
||
/// </summary>
|
||
/// <param name="moduleIndex">板卡序号 (1 - 3)</param>
|
||
/// <returns>对应的 IO 板卡实例</returns>
|
||
/// <exception cref="ArgumentOutOfRangeException">板卡序号不在 1-3 范围内时抛出</exception>
|
||
/// <exception cref="InvalidOperationException">板卡实例未初始化时抛出</exception>
|
||
private IOBoard GetBoardInstance(int moduleIndex)
|
||
{
|
||
IOBoard board = moduleIndex switch
|
||
{
|
||
1 => Board1,
|
||
2 => Board2,
|
||
3 => Board3,
|
||
_ => throw new ArgumentOutOfRangeException(nameof(moduleIndex), "非法的板卡序号,系统仅支持 1#、2# 和 3# 模块。")
|
||
};
|
||
|
||
return board ?? throw new InvalidOperationException($"IO板卡模块 [{moduleIndex}#] 尚未完成初始化,无法建立总线通信。");
|
||
}
|
||
}
|
||
}
|