using System;
|
|
namespace Aging.Infrastructure.Contracts
|
{
|
/// <summary>
|
/// 资源键值对象,唯一标识一个可管理的硬件资源。
|
/// 格式: "DeviceId:Channel",如 "PSU024:CH1"、"CAN_CARD01:CH2"、"PLC01:OUTPUT_GROUP_A"
|
/// </summary>
|
public sealed class ResourceKey : IEquatable<ResourceKey>
|
{
|
/// <summary>
|
/// 设备标识符
|
/// </summary>
|
public string DeviceId { get; }
|
|
/// <summary>
|
/// 通道标识(可选)
|
/// </summary>
|
public string? Channel { get; }
|
|
/// <summary>
|
/// 资源类型(PowerSupply/CAN/PLC/Chamber等)
|
/// </summary>
|
public string ResourceType { get; }
|
|
/// <summary>
|
/// 初始化资源键实例
|
/// </summary>
|
/// <param name="deviceId">设备标识符</param>
|
/// <param name="resourceType">资源类型</param>
|
/// <param name="channel">通道标识(可选)</param>
|
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;
|
}
|
|
/// <summary>
|
/// 从字符串解析资源键,格式为 "DeviceId:Channel"
|
/// </summary>
|
/// <param name="key">资源键字符串</param>
|
/// <returns>解析后的资源键实例</returns>
|
/// <exception cref="ArgumentException">格式无效时抛出</exception>
|
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);
|
}
|
|
/// <summary>
|
/// 获取操作资源键,用于物理会话锁
|
/// </summary>
|
public string OperationResourceKey => $"{DeviceId}:SESSION";
|
|
/// <summary>
|
/// 根据设备ID前缀推断资源类型
|
/// </summary>
|
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";
|
}
|
|
/// <inheritdoc/>
|
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);
|
}
|
|
/// <inheritdoc/>
|
public override bool Equals(object? obj) => Equals(obj as ResourceKey);
|
|
/// <inheritdoc/>
|
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;
|
}
|
}
|
|
/// <inheritdoc/>
|
public override string ToString() =>
|
string.IsNullOrEmpty(Channel) ? DeviceId : $"{DeviceId}:{Channel}";
|
|
/// <summary>
|
/// 相等运算符重载
|
/// </summary>
|
public static bool operator ==(ResourceKey? left, ResourceKey? right) =>
|
Equals(left, right);
|
|
/// <summary>
|
/// 不等运算符重载
|
/// </summary>
|
public static bool operator !=(ResourceKey? left, ResourceKey? right) =>
|
!Equals(left, right);
|
}
|
}
|