Files
SecMPS/gateway/src/IntegrationGateway.Adapters.MC4/Mc4Adapter.cs

282 lines
11 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 IntegrationGateway.Core.Abstractions;
using IntegrationGateway.Core.Infrastructure;
using IntegrationGateway.Core.Models;
using System.Text;
using System.Text.Json;
namespace IntegrationGateway.Adapters.MC4;
/// <summary>
/// MC4.0 动环监控子系统适配器。
///
/// 实现的能力接口:
/// - IHasOwnDeviceTree对象树区域→设备层级
/// - IHasPoints实时点位值读取 + 反向控制写值
/// - IHasAlarms告警查询、确认、结束
///
/// 限流2 QPSMC4.0 API 推荐值)
/// 分页转换:网关 page/size ↔ MC4.0 skip/limit
/// </summary>
public class Mc4Adapter : IHasOwnDeviceTree, IHasPoints, IHasAlarms
{
private readonly HttpClient _http;
private readonly Mc4AuthHelper _auth;
/// <summary>令牌桶限流器2 QPS</summary>
private readonly RateLimiter _limiter = new(2);
/// <summary>适配器编码,格式 "MC4:实例名"</summary>
public string AdapterCode { get; }
/// <summary>人类可读的适配器名称</summary>
public string DisplayName => $"MC4 ({AdapterCode})";
/// <summary>适配器能力声明</summary>
public AdapterCapabilities Capabilities => new()
{
HasObjectTree = true, HasPoints = true, HasAlarms = true, AcceptsControl = true
};
/// <summary>创建 Mc4Adapter 实例</summary>
/// <param name="adapterCode">适配器编码</param>
/// <param name="http">HttpClient 实例</param>
/// <param name="baseUrl">MC4.0 服务地址</param>
public Mc4Adapter(string adapterCode, HttpClient http, string baseUrl)
{
AdapterCode = adapterCode;
_http = http;
_auth = new Mc4AuthHelper(http, baseUrl);
}
/// <summary>初始化适配器:获取 MC4.0 Token</summary>
public async Task InitializeAsync() => await _auth.GetTokenAsync();
/// <summary>健康检查:尝试调用 MC4.0 认证接口确认可达性</summary>
public async Task<bool> HealthCheckAsync()
{
try
{
var client = await _auth.GetAuthenticatedClientAsync();
var resp = await client.PostAsync("/api/central/auth/conf/get", null);
return resp.IsSuccessStatusCode;
}
catch { return false; }
}
// ═══════════════════════════════════════════
// IHasOwnDeviceTree 实现
// ═══════════════════════════════════════════
/// <summary>
/// 获取 MC4.0 完整对象树。
/// Type=1 的节点为区域Type=2 的节点为设备。
/// </summary>
public async Task<List<DeviceTreeNode>> GetObjectTreeAsync()
{
await _limiter.WaitAsync();
var client = await _auth.GetAuthenticatedClientAsync();
var resp = await client.PostAsync("/api/central/object/tree", null);
resp.EnsureSuccessStatusCode();
var json = await resp.Content.ReadAsStringAsync();
var tree = JsonSerializer.Deserialize<List<Mc4TreeNode>>(json)!;
return tree.Select(MapNode).ToList();
}
/// <summary>MC4.0 树节点 → DeviceTreeNode 映射</summary>
private static DeviceTreeNode MapNode(Mc4TreeNode n) => new()
{
Id = n.Id,
SourceId = n.Id.ToString(),
Name = n.Name ?? n.Id.ToString(),
Type = n.Type,
ObjectType = n.ObjectType,
Tag = n.Tag,
Option = n.Option ?? new Dictionary<string, object?>(),
Children = n.Children?.Select(MapNode).ToList() ?? new()
};
// ═══════════════════════════════════════════
// IHasPoints 实现
// ═══════════════════════════════════════════
/// <summary>获取指定设备的所有实时点位值</summary>
public async Task<List<PointValue>> GetRealtimeValuesAsync(string sourceDeviceId)
{
await _limiter.WaitAsync();
var client = await _auth.GetAuthenticatedClientAsync();
var body = JsonSerializer.Serialize(new { id = int.Parse(sourceDeviceId) });
var resp = await client.PostAsync("/api/central/device/point/value/get",
new StringContent(body, Encoding.UTF8, "application/json"));
resp.EnsureSuccessStatusCode();
var json = await resp.Content.ReadAsStringAsync();
var values = JsonSerializer.Deserialize<List<Mc4PointValue>>(json)!;
return values.Select(v => new PointValue
{
SourceDeviceId = sourceDeviceId,
PointIndex = v.Index,
Value = v.Value,
UpdateTime = v.Time != null ? DateTime.Parse(v.Time) : null,
Interval = v.Interval
}).ToList();
}
/// <summary>向指定设备的指定点位写入控制值</summary>
public async Task SetPointValueAsync(string sourceDeviceId, int pointIndex, double value)
{
await _limiter.WaitAsync();
var client = await _auth.GetAuthenticatedClientAsync();
var body = JsonSerializer.Serialize(new { id = int.Parse(sourceDeviceId), index = pointIndex, value });
await client.PostAsync("/api/central/point/value/set",
new StringContent(body, Encoding.UTF8, "application/json"));
}
// ═══════════════════════════════════════════
// IHasAlarms 实现
// ═══════════════════════════════════════════
/// <summary>
/// 分页查询告警列表。
/// 内部完成 page/size → skip/limit 转换。
/// </summary>
public async Task<PagedResult<StandardAlarm>> GetAlarmsAsync(int page, int size, DateTime from, DateTime to,
string? level = null, string? state = null)
{
await _limiter.WaitAsync();
var client = await _auth.GetAuthenticatedClientAsync();
var body = JsonSerializer.Serialize(new Mc4AlarmQuery
{
From = from.ToString("yyyy-MM-dd HH:mm:ss"),
To = to.ToString("yyyy-MM-dd HH:mm:ss"),
Skip = (page - 1) * size,
Limit = size,
Sort = 1 // 按时间降序
});
var resp = await client.PostAsync("/api/central/alarm/query",
new StringContent(body, Encoding.UTF8, "application/json"));
resp.EnsureSuccessStatusCode();
var json = await resp.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<Mc4AlarmQueryResult>(json)!;
return new PagedResult<StandardAlarm>
{
Items = result.List?.Select(a => new StandardAlarm
{
AlarmId = a.Id ?? "",
DeviceId = a.Sid?.ToString(),
AdapterCode = AdapterCode,
Level = MapAlarmLevel(a.Level),
Title = a.Desc ?? "",
OccurTime = DateTime.TryParse(a.Stime, out var st) ? st : DateTime.MinValue,
Status = MapAlarmState(a.State),
ActualValue = a.Soption?.Value,
ThresholdValue = a.Eoption?.Value
}).ToList() ?? new(),
Total = result.Total
};
}
/// <summary>确认告警(同时写回 MC4.0</summary>
public async Task ConfirmAlarmAsync(string alarmId)
{
await _limiter.WaitAsync();
var client = await _auth.GetAuthenticatedClientAsync();
var body = JsonSerializer.Serialize(new { id = alarmId, option = new { } });
await client.PostAsync("/api/central/alarm/confirm",
new StringContent(body, Encoding.UTF8, "application/json"));
}
/// <summary>结束告警(同时写回 MC4.0</summary>
public async Task EndAlarmAsync(string alarmId)
{
await _limiter.WaitAsync();
var client = await _auth.GetAuthenticatedClientAsync();
var body = JsonSerializer.Serialize(new { id = alarmId, option = new { } });
await client.PostAsync("/api/central/alarm/end",
new StringContent(body, Encoding.UTF8, "application/json"));
}
/// <summary>MC4.0 告警等级数字 → 中文映射</summary>
private static string MapAlarmLevel(int level) => level switch
{
1 => "提示", 2 => "普通", 3 => "重要", 4 => "紧急", _ => "提示"
};
/// <summary>MC4.0 告警状态数字 → 中文映射</summary>
private static string MapAlarmState(int state) => state switch
{
1 => "未确认", 2 => "已确认", 3 => "已结束", _ => "未确认"
};
}
// ═══════════════════════════════════════════
// MC4.0 JSON 反序列化模型(内部使用)
// ═══════════════════════════════════════════
/// <summary>MC4.0 对象树节点</summary>
public class Mc4TreeNode
{
public int Id { get; set; }
public string? Name { get; set; }
/// <summary>节点类型1=区域2=设备</summary>
public int Type { get; set; }
public int ObjectType { get; set; }
public string? Tag { get; set; }
public Dictionary<string, object?>? Option { get; set; }
public List<Mc4TreeNode>? Children { get; set; }
}
/// <summary>MC4.0 点位值</summary>
public class Mc4PointValue
{
public int Id { get; set; }
public int Index { get; set; }
public double Value { get; set; }
public string? Time { get; set; }
public int Interval { get; set; }
}
/// <summary>MC4.0 告警查询请求体</summary>
public class Mc4AlarmQuery
{
public string? Sid { get; set; }
public string From { get; set; } = "";
public string To { get; set; } = "";
/// <summary>跳过的记录数(= (page-1)*size</summary>
public int Skip { get; set; }
/// <summary>每页条数</summary>
public int Limit { get; set; }
/// <summary>排序方式1=时间降序</summary>
public int Sort { get; set; }
}
/// <summary>MC4.0 告警查询响应</summary>
public class Mc4AlarmQueryResult
{
public int Total { get; set; }
public List<Mc4AlarmItem>? List { get; set; }
}
/// <summary>MC4.0 告警条目</summary>
public class Mc4AlarmItem
{
public string? Id { get; set; }
/// <summary>设备 SID</summary>
public int? Sid { get; set; }
public string? Desc { get; set; }
public string? EngDesc { get; set; }
public int Level { get; set; }
public int State { get; set; }
public string? Stime { get; set; }
public string? Etime { get; set; }
public string? Ctime { get; set; }
public string? Cuser { get; set; }
public int Type { get; set; }
/// <summary>告警触发时阈值信息</summary>
public Mc4Option? Soption { get; set; }
/// <summary>告警结束时阈值信息</summary>
public Mc4Option? Eoption { get; set; }
}
/// <summary>MC4.0 告警阈值信息</summary>
public class Mc4Option
{
public double? Value { get; set; }
public string? TypeName { get; set; }
}