187 lines
10 KiB
C#
187 lines
10 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接线图 更新)
|
|
/// </summary>
|
|
public enum 功能动作 : byte
|
|
{
|
|
产品充电三相,
|
|
产品放电三相,
|
|
KL30通断,
|
|
KL15通断,
|
|
电子锁60欧电阻,
|
|
温度检测1K欧电阻,
|
|
CC信号串1K欧电阻,
|
|
CP信号串1K欧电阻,
|
|
CC信号串680欧电阻,
|
|
CC信号串220欧电阻,
|
|
CC信号串100欧电阻,
|
|
CC信号空载,
|
|
蜂鸣器报警,
|
|
抛负载通断, // (接线图中标注于 Y14, KM8)
|
|
短路测试HV, // (接线图中标注于 Y15, KM9)
|
|
短路测试LV // (接线图中标注于 Y16, KM10)
|
|
}
|
|
|
|
/// <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;
|
|
}
|
|
}
|
|
|
|
[ACPCommand]
|
|
public class IOBoardGroup
|
|
{
|
|
// 声明底层的 3 个 IO 板卡实例(对应三工位系统)
|
|
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硬件接线图 严格对齐的数据字典 (3个模块独立对应1-3工位)
|
|
private static readonly Dictionary<int, Dictionary<功能动作, IoPortInfo>> IoMapping
|
|
= new Dictionary<int, Dictionary<功能动作, IoPortInfo>>()
|
|
{
|
|
// 1# 工位 -> IO继电器模块 1#
|
|
{ 1, new Dictionary<功能动作, IoPortInfo> {
|
|
{ 功能动作.产品充电三相, new IoPortInfo(1, 0) }, // Y1 -> 0 (KM2)
|
|
{ 功能动作.产品放电三相, new IoPortInfo(1, 1) }, // Y2 -> 1 (KM3)
|
|
{ 功能动作.KL30通断, new IoPortInfo(1, 2) }, // Y3 -> 2
|
|
{ 功能动作.KL15通断, new IoPortInfo(1, 3) }, // Y4 -> 3
|
|
{ 功能动作.电子锁60欧电阻, new IoPortInfo(1, 4) }, // Y5 -> 4
|
|
{ 功能动作.温度检测1K欧电阻, new IoPortInfo(1, 5) }, // Y6 -> 5
|
|
{ 功能动作.CC信号串1K欧电阻, new IoPortInfo(1, 6) }, // Y7 -> 6
|
|
{ 功能动作.CP信号串1K欧电阻, new IoPortInfo(1, 7) }, // Y8 -> 7
|
|
{ 功能动作.CC信号串680欧电阻, new IoPortInfo(1, 8) }, // Y9 -> 8
|
|
{ 功能动作.CC信号串220欧电阻, new IoPortInfo(1, 9) }, // Y10 -> 9
|
|
{ 功能动作.CC信号串100欧电阻, new IoPortInfo(1, 10) }, // Y11 -> 10
|
|
{ 功能动作.CC信号空载, new IoPortInfo(1, 11) }, // Y12 -> 11
|
|
{ 功能动作.蜂鸣器报警, new IoPortInfo(1, 12) }, // Y13 -> 12
|
|
{ 功能动作.抛负载通断, new IoPortInfo(3, 13) }, // Y14 -> 13 (KM8)
|
|
{ 功能动作.短路测试HV, new IoPortInfo(3, 14) }, // Y15 -> 14 (KM9)
|
|
{ 功能动作.短路测试LV, new IoPortInfo(3, 15) } // Y16 -> 15 (KM10)
|
|
}},
|
|
// 2# 工位 -> IO继电器模块 2#
|
|
{ 2, new Dictionary<功能动作, IoPortInfo> {
|
|
{ 功能动作.产品充电三相, new IoPortInfo(2, 0) }, // Y1 -> 0 (KM4)
|
|
{ 功能动作.产品放电三相, new IoPortInfo(2, 1) }, // Y2 -> 1 (KM5)
|
|
{ 功能动作.KL30通断, new IoPortInfo(2, 2) }, // Y3 -> 2
|
|
{ 功能动作.KL15通断, new IoPortInfo(2, 3) }, // Y4 -> 3
|
|
{ 功能动作.电子锁60欧电阻, new IoPortInfo(2, 4) }, // Y5 -> 4
|
|
{ 功能动作.温度检测1K欧电阻, new IoPortInfo(2, 5) }, // Y6 -> 5
|
|
{ 功能动作.CC信号串1K欧电阻, new IoPortInfo(2, 6) }, // Y7 -> 6
|
|
{ 功能动作.CP信号串1K欧电阻, new IoPortInfo(2, 7) }, // Y8 -> 7
|
|
{ 功能动作.CC信号串680欧电阻, new IoPortInfo(2, 8) }, // Y9 -> 8
|
|
{ 功能动作.CC信号串220欧电阻, new IoPortInfo(2, 9) }, // Y10 -> 9
|
|
{ 功能动作.CC信号串100欧电阻, new IoPortInfo(2, 10) }, // Y11 -> 10
|
|
{ 功能动作.CC信号空载, new IoPortInfo(2, 11) }, // Y12 -> 11
|
|
{ 功能动作.蜂鸣器报警, new IoPortInfo(2, 12) }, // Y13 -> 12
|
|
{ 功能动作.抛负载通断, new IoPortInfo(3, 13) }, // Y14 -> 13 (KM8)
|
|
{ 功能动作.短路测试HV, new IoPortInfo(3, 14) }, // Y15 -> 14 (KM9)
|
|
{ 功能动作.短路测试LV, new IoPortInfo(3, 15) } // Y16 -> 15 (KM10)
|
|
}},
|
|
// 3# 工位 -> IO继电器模块 3# (包含额外的抛负载及短路测试控制)
|
|
{ 3, new Dictionary<功能动作, IoPortInfo> {
|
|
{ 功能动作.产品充电三相, new IoPortInfo(3, 0) }, // Y1 -> 0 (KM6)
|
|
{ 功能动作.产品放电三相, new IoPortInfo(3, 1) }, // Y2 -> 1 (KM7)
|
|
{ 功能动作.KL30通断, new IoPortInfo(3, 2) }, // Y3 -> 2
|
|
{ 功能动作.KL15通断, new IoPortInfo(3, 3) }, // Y4 -> 3
|
|
{ 功能动作.电子锁60欧电阻, new IoPortInfo(3, 4) }, // Y5 -> 4
|
|
{ 功能动作.温度检测1K欧电阻, new IoPortInfo(3, 5) }, // Y6 -> 5
|
|
{ 功能动作.CC信号串1K欧电阻, new IoPortInfo(3, 6) }, // Y7 -> 6
|
|
{ 功能动作.CP信号串1K欧电阻, new IoPortInfo(3, 7) }, // Y8 -> 7
|
|
{ 功能动作.CC信号串680欧电阻, new IoPortInfo(3, 8) }, // Y9 -> 8
|
|
{ 功能动作.CC信号串220欧电阻, new IoPortInfo(3, 9) }, // Y10 -> 9
|
|
{ 功能动作.CC信号串100欧电阻, new IoPortInfo(3, 10) }, // Y11 -> 10
|
|
{ 功能动作.CC信号空载, new IoPortInfo(3, 11) }, // Y12 -> 11
|
|
{ 功能动作.蜂鸣器报警, new IoPortInfo(3, 12) }, // Y13 -> 12
|
|
{ 功能动作.抛负载通断, new IoPortInfo(3, 13) }, // Y14 -> 13 (KM8)
|
|
{ 功能动作.短路测试HV, new IoPortInfo(3, 14) }, // Y15 -> 14 (KM9)
|
|
{ 功能动作.短路测试LV, new IoPortInfo(3, 15) } // Y16 -> 15 (KM10)
|
|
}}
|
|
};
|
|
|
|
// 修改构造函数以支持 3 个板块
|
|
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 点位映射配置。请注意抛负载及短路测试仅存在于 3# 工位。");
|
|
}
|
|
|
|
// 2. 软件防错高危互锁逻辑:防止同时执行充电和放电
|
|
if (开关状态)
|
|
{
|
|
if (动作 == 功能动作.产品充电三相)
|
|
{
|
|
if (IoMapping[台架序号].TryGetValue(功能动作.产品放电三相, out var 放电点位))
|
|
{
|
|
await GetBoardInstance(放电点位.ModuleIndex).写输出开关(_slaveAddress, 放电点位.Address, false);
|
|
}
|
|
}
|
|
else if (动作 == 功能动作.产品放电三相)
|
|
{
|
|
if (IoMapping[台架序号].TryGetValue(功能动作.产品充电三相, out var 充电点位))
|
|
{
|
|
await GetBoardInstance(充电点位.ModuleIndex).写输出开关(_slaveAddress, 充电点位.Address, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. 动态获取目标板卡实例并直接驱动开关端口
|
|
IOBoard 目标板卡 = GetBoardInstance(io信息.ModuleIndex);
|
|
await 目标板卡.写输出开关(_slaveAddress, io信息.Address, 开关状态);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 内部路由辅助方法:根据板卡索引安全获取当前连接的实例对象
|
|
/// </summary>
|
|
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}#] 尚未完成初始化,无法建立总线通信。");
|
|
}
|
|
}
|
|
} |