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; }
}