Files
ACP/DeviceCommand/Devices/IOBoardGroup.cs
T

192 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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}#] 尚未完成初始化,无法建立总线通信。");
}
}
}