PhaseG3: MC4Adapter — Token认证+3接口实现 编译通过

This commit is contained in:
2026-05-17 04:28:40 +08:00
parent 3f184581eb
commit 63e0c819f6
5 changed files with 259 additions and 0 deletions

View File

@@ -1,5 +1,6 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/IntegrationGateway.Adapters.MC4/IntegrationGateway.Adapters.MC4.csproj" />
<Project Path="src/IntegrationGateway.Adapters.Owl/IntegrationGateway.Adapters.Owl.csproj" />
</Folder>
<Project Path="src/IntegrationGateway.Core/IntegrationGateway.Core.csproj" />

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\IntegrationGateway.Core\IntegrationGateway.Core.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,203 @@
using IntegrationGateway.Core.Abstractions;
using IntegrationGateway.Core.Infrastructure;
using IntegrationGateway.Core.Models;
using System.Text;
using System.Text.Json;
namespace IntegrationGateway.Adapters.MC4;
public class Mc4Adapter : IHasOwnDeviceTree, IHasPoints, IHasAlarms
{
private readonly HttpClient _http;
private readonly Mc4AuthHelper _auth;
private readonly RateLimiter _limiter = new(2);
public string AdapterCode { get; }
public string DisplayName => $"MC4 ({AdapterCode})";
public AdapterCapabilities Capabilities => new()
{
HasObjectTree = true, HasPoints = true, HasAlarms = true, AcceptsControl = true
};
public Mc4Adapter(string adapterCode, HttpClient http, string baseUrl)
{
AdapterCode = adapterCode;
_http = http;
_auth = new Mc4AuthHelper(http, baseUrl);
}
public async Task InitializeAsync() => await _auth.GetTokenAsync();
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 ───
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();
}
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 ───
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();
}
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 ───
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
};
}
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"));
}
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"));
}
private static string MapAlarmLevel(int level) => level switch { 1 => "提示", 2 => "普通", 3 => "重要", 4 => "紧急", _ => "提示" };
private static string MapAlarmState(int state) => state switch { 1 => "未确认", 2 => "已确认", 3 => "已结束", _ => "未确认" };
}
// ─── MC4 JSON Models ───
public class Mc4TreeNode
{
public int Id { get; set; }
public string? Name { get; set; }
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; }
}
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; }
}
public class Mc4AlarmQuery
{
public string? Sid { get; set; }
public string From { get; set; } = "";
public string To { get; set; } = "";
public int Skip { get; set; }
public int Limit { get; set; }
public int Sort { get; set; }
}
public class Mc4AlarmQueryResult
{
public int Total { get; set; }
public List<Mc4AlarmItem>? List { get; set; }
}
public class Mc4AlarmItem
{
public string? Id { get; set; }
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; }
}
public class Mc4Option
{
public double? Value { get; set; }
public string? TypeName { get; set; }
}

View File

@@ -0,0 +1,41 @@
using System.Text.Json;
namespace IntegrationGateway.Adapters.MC4;
public class Mc4AuthHelper
{
private readonly HttpClient _http;
private readonly string _baseUrl;
private string? _token;
private DateTime _tokenExpiry = DateTime.MinValue;
public Mc4AuthHelper(HttpClient http, string baseUrl)
{
_http = http; _baseUrl = baseUrl.TrimEnd('/');
}
public async Task<string> GetTokenAsync()
{
if (!string.IsNullOrEmpty(_token) && DateTime.UtcNow < _tokenExpiry) return _token;
var resp = await _http.PostAsync($"{_baseUrl}/api/central/auth/conf/get", null);
resp.EnsureSuccessStatusCode();
var json = await resp.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<Mc4AuthResponse>(json);
_token = result?.Token ?? "";
_tokenExpiry = DateTime.UtcNow.AddHours(8);
return _token!;
}
public async Task<HttpClient> GetAuthenticatedClientAsync()
{
var token = await GetTokenAsync();
var client = new HttpClient { BaseAddress = new Uri(_baseUrl) };
client.DefaultRequestHeaders.Add("token", token);
return client;
}
public void Invalidate() => _token = null;
public class Mc4AuthResponse { public string? Token { get; set; } }
}

View File

@@ -3,6 +3,7 @@
<ItemGroup>
<ProjectReference Include="..\IntegrationGateway.Core\IntegrationGateway.Core.csproj" />
<ProjectReference Include="..\IntegrationGateway.Adapters.Owl\IntegrationGateway.Adapters.Owl.csproj" />
<ProjectReference Include="..\IntegrationGateway.Adapters.MC4\IntegrationGateway.Adapters.MC4.csproj" />
</ItemGroup>
<PropertyGroup>