using IntegrationGateway.Core.Abstractions; using IntegrationGateway.Core.Infrastructure; using IntegrationGateway.Core.Models; using System.Text; using System.Text.Json; namespace IntegrationGateway.Adapters.MC4; /// /// MC4.0 动环监控子系统适配器。 /// /// 实现的能力接口: /// - IHasOwnDeviceTree:对象树(区域→设备层级) /// - IHasPoints:实时点位值读取 + 反向控制写值 /// - IHasAlarms:告警查询、确认、结束 /// /// 限流:2 QPS(MC4.0 API 推荐值) /// 分页转换:网关 page/size ↔ MC4.0 skip/limit /// public class Mc4Adapter : IHasOwnDeviceTree, IHasPoints, IHasAlarms { private readonly HttpClient _http; private readonly Mc4AuthHelper _auth; /// 令牌桶限流器(2 QPS) private readonly RateLimiter _limiter = new(2); /// 适配器编码,格式 "MC4:实例名" public string AdapterCode { get; } /// 人类可读的适配器名称 public string DisplayName => $"MC4 ({AdapterCode})"; /// 适配器能力声明 public AdapterCapabilities Capabilities => new() { HasObjectTree = true, HasPoints = true, HasAlarms = true, AcceptsControl = true }; /// 创建 Mc4Adapter 实例 /// 适配器编码 /// HttpClient 实例 /// MC4.0 服务地址 public Mc4Adapter(string adapterCode, HttpClient http, string baseUrl) { AdapterCode = adapterCode; _http = http; _auth = new Mc4AuthHelper(http, baseUrl); } /// 初始化适配器:获取 MC4.0 Token public async Task InitializeAsync() => await _auth.GetTokenAsync(); /// 健康检查:尝试调用 MC4.0 认证接口确认可达性 public async Task 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 实现 // ═══════════════════════════════════════════ /// /// 获取 MC4.0 完整对象树。 /// Type=1 的节点为区域,Type=2 的节点为设备。 /// public async Task> 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>(json)!; return tree.Select(MapNode).ToList(); } /// MC4.0 树节点 → DeviceTreeNode 映射 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(), Children = n.Children?.Select(MapNode).ToList() ?? new() }; // ═══════════════════════════════════════════ // IHasPoints 实现 // ═══════════════════════════════════════════ /// 获取指定设备的所有实时点位值 public async Task> 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>(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(); } /// 向指定设备的指定点位写入控制值 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 实现 // ═══════════════════════════════════════════ /// /// 分页查询告警列表。 /// 内部完成 page/size → skip/limit 转换。 /// public async Task> 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(json)!; return new PagedResult { 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 }; } /// 确认告警(同时写回 MC4.0) 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")); } /// 结束告警(同时写回 MC4.0) 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")); } /// MC4.0 告警等级数字 → 中文映射 private static string MapAlarmLevel(int level) => level switch { 1 => "提示", 2 => "普通", 3 => "重要", 4 => "紧急", _ => "提示" }; /// MC4.0 告警状态数字 → 中文映射 private static string MapAlarmState(int state) => state switch { 1 => "未确认", 2 => "已确认", 3 => "已结束", _ => "未确认" }; } // ═══════════════════════════════════════════ // MC4.0 JSON 反序列化模型(内部使用) // ═══════════════════════════════════════════ /// MC4.0 对象树节点 public class Mc4TreeNode { public int Id { get; set; } public string? Name { get; set; } /// 节点类型:1=区域,2=设备 public int Type { get; set; } public int ObjectType { get; set; } public string? Tag { get; set; } public Dictionary? Option { get; set; } public List? Children { get; set; } } /// MC4.0 点位值 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; } } /// MC4.0 告警查询请求体 public class Mc4AlarmQuery { public string? Sid { get; set; } public string From { get; set; } = ""; public string To { get; set; } = ""; /// 跳过的记录数(= (page-1)*size) public int Skip { get; set; } /// 每页条数 public int Limit { get; set; } /// 排序方式:1=时间降序 public int Sort { get; set; } } /// MC4.0 告警查询响应 public class Mc4AlarmQueryResult { public int Total { get; set; } public List? List { get; set; } } /// MC4.0 告警条目 public class Mc4AlarmItem { public string? Id { get; set; } /// 设备 SID 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; } /// 告警触发时阈值信息 public Mc4Option? Soption { get; set; } /// 告警结束时阈值信息 public Mc4Option? Eoption { get; set; } } /// MC4.0 告警阈值信息 public class Mc4Option { public double? Value { get; set; } public string? TypeName { get; set; } }