完善 MCS 桌面代理与开炼机动作同步,修复原料相关菜单权限,桌面端新增上辅机 MES 菜单。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-27 15:56:05 +08:00
parent 7a9c19e4f3
commit 442a4c8ae2
113 changed files with 10169 additions and 91 deletions

View File

@@ -9,10 +9,11 @@ using YY.Admin.Core.Services;
using YY.Admin.Core.Session;
using YY.Admin.Helper;
using YY.Admin.Infrastructure.Storage;
using YY.Admin.Services.Service.EquipmentDb;
namespace YY.Admin.Infrastructure.Hubs;
public class StompWebSocketService : ISignalRService
public class StompWebSocketService : ISignalRService, IEquipmentDbStompSender
{
// STOMP heart-beat: send \n every 10 s, declare we want to receive every 10 s
private const int HeartbeatMs = 10_000;
@@ -188,6 +189,11 @@ public class StompWebSocketService : ISignalRService
BuildSubscribeFrame("sub-device-pong", $"/topic/device/{_deviceId}/pong"),
cancellationToken).ConfigureAwait(false);
// 设备库命令通道(匿名/登录均订阅)
await SendFrameAsync(
BuildSubscribeFrame("sub-device-eq-cmd", $"/topic/device/{_deviceId}/cmd"),
cancellationToken).ConfigureAwait(false);
if (!anonymous && !string.IsNullOrWhiteSpace(_token))
{
await SendFrameAsync(
@@ -207,6 +213,25 @@ public class StompWebSocketService : ISignalRService
_networkMonitor.SetStompTransportOnline(true);
_ = Task.Run(() => ReceiveLoopAsync(cts.Token), cts.Token);
// 连接成功后上报设备代理状态
_ = Task.Run(async () =>
{
try
{
await SendAgentStatusAsync(new
{
deviceId = _deviceId,
hostName = Environment.MachineName,
dbConnected = false,
dbMessage = "pending",
ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
}, CancellationToken.None).ConfigureAwait(false);
}
catch
{
// ignore
}
});
return;
}
catch
@@ -232,6 +257,38 @@ public class StompWebSocketService : ISignalRService
await SendFrameAsync(frame, cancellationToken).ConfigureAwait(false);
}
public string CurrentDeviceId => _deviceId;
public async Task SendAgentStatusAsync(object status, CancellationToken cancellationToken = default)
{
if (IsDisconnectedByUser() || _socket == null || _socket.State != WebSocketState.Open)
{
return;
}
var json = System.Text.Json.JsonSerializer.Serialize(status);
var frame = "SEND\n" +
"destination:/app/device/agent/status\n" +
"content-type:application/json\n" +
$"content-length:{Encoding.UTF8.GetByteCount(json)}\n\n" +
$"{json}\0";
await SendFrameAsync(frame, cancellationToken).ConfigureAwait(false);
}
public async Task SendAgentResultAsync(object result, CancellationToken cancellationToken = default)
{
if (IsDisconnectedByUser() || _socket == null || _socket.State != WebSocketState.Open)
{
return;
}
var json = System.Text.Json.JsonSerializer.Serialize(result);
var frame = "SEND\n" +
"destination:/app/device/agent/result\n" +
"content-type:application/json\n" +
$"content-length:{Encoding.UTF8.GetByteCount(json)}\n\n" +
$"{json}\0";
await SendFrameAsync(frame, cancellationToken).ConfigureAwait(false);
}
public async Task DisconnectAsync(CancellationToken cancellationToken = default)
{
var cts = Interlocked.Exchange(ref _connectionCts, null);
@@ -437,8 +494,29 @@ public class StompWebSocketService : ISignalRService
return "ws://" + baseUrl["http://".Length..] + "/ws/device";
}
private static string ResolveDeviceId(string token)
private string ResolveDeviceId(string token)
{
try
{
var fromConfig = EquipmentDbSettingsStore.Load().DeviceId;
if (!string.IsNullOrWhiteSpace(fromConfig))
{
return fromConfig.Trim();
}
}
catch
{
// ignore
}
var configured = _configuration.GetValue<string>("EquipmentDb:DeviceId");
if (!string.IsNullOrWhiteSpace(configured))
{
return configured.Trim();
}
if (!string.IsNullOrWhiteSpace(Environment.MachineName))
{
return Environment.MachineName;
}
try
{
var parts = token.Split('.');