using System; namespace Aging.Infrastructure.Contracts { /// /// 资源键值对象,唯一标识一个可管理的硬件资源。 /// 格式: "DeviceId:Channel",如 "PSU024:CH1"、"CAN_CARD01:CH2"、"PLC01:OUTPUT_GROUP_A" /// public sealed class ResourceKey : IEquatable { /// /// 设备标识符 /// public string DeviceId { get; } /// /// 通道标识(可选) /// public string? Channel { get; } /// /// 资源类型(PowerSupply/CAN/PLC/Chamber等) /// public string ResourceType { get; } /// /// 初始化资源键实例 /// /// 设备标识符 /// 资源类型 /// 通道标识(可选) public ResourceKey(string deviceId, string resourceType, string? channel = null) { DeviceId = deviceId ?? throw new ArgumentNullException(nameof(deviceId)); ResourceType = resourceType ?? throw new ArgumentNullException(nameof(resourceType)); Channel = channel; } /// /// 从字符串解析资源键,格式为 "DeviceId:Channel" /// /// 资源键字符串 /// 解析后的资源键实例 /// 格式无效时抛出 public static ResourceKey FromString(string key) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("资源键不能为空", nameof(key)); var parts = key.Split(':'); if (parts.Length < 2) throw new ArgumentException($"无效的资源键格式: '{key}',期望格式为 'DeviceId:Channel'"); var deviceId = parts[0].Trim(); var channel = string.Join(":", parts.Skip(1)).Trim(); if (string.IsNullOrEmpty(deviceId)) throw new ArgumentException($"无效的资源键格式: '{key}',DeviceId 不能为空"); // 根据设备ID前缀推断资源类型 var resourceType = InferResourceType(deviceId); return new ResourceKey(deviceId, resourceType, string.IsNullOrEmpty(channel) ? null : channel); } /// /// 获取操作资源键,用于物理会话锁 /// public string OperationResourceKey => $"{DeviceId}:SESSION"; /// /// 根据设备ID前缀推断资源类型 /// private static string InferResourceType(string deviceId) { var upper = deviceId.ToUpperInvariant(); if (upper.StartsWith("PSU") || upper.StartsWith("POWER")) return "PowerSupply"; if (upper.StartsWith("CAN")) return "CAN"; if (upper.StartsWith("PLC")) return "PLC"; if (upper.StartsWith("CHAMBER") || upper.StartsWith("TEMP")) return "Chamber"; if (upper.StartsWith("DAQ")) return "DAQ"; if (upper.StartsWith("SERIAL") || upper.StartsWith("COM")) return "Serial"; return "Generic"; } /// public bool Equals(ResourceKey? other) { if (other is null) return false; if (ReferenceEquals(this, other)) return true; return string.Equals(DeviceId, other.DeviceId, StringComparison.OrdinalIgnoreCase) && string.Equals(Channel, other.Channel, StringComparison.OrdinalIgnoreCase) && string.Equals(ResourceType, other.ResourceType, StringComparison.OrdinalIgnoreCase); } /// public override bool Equals(object? obj) => Equals(obj as ResourceKey); /// public override int GetHashCode() { unchecked { int hash = 17; hash = hash * 31 + (DeviceId?.ToUpperInvariant().GetHashCode() ?? 0); hash = hash * 31 + (Channel?.ToUpperInvariant().GetHashCode() ?? 0); hash = hash * 31 + (ResourceType?.ToUpperInvariant().GetHashCode() ?? 0); return hash; } } /// public override string ToString() => string.IsNullOrEmpty(Channel) ? DeviceId : $"{DeviceId}:{Channel}"; /// /// 相等运算符重载 /// public static bool operator ==(ResourceKey? left, ResourceKey? right) => Equals(left, right); /// /// 不等运算符重载 /// public static bool operator !=(ResourceKey? left, ResourceKey? right) => !Equals(left, right); } }