@using MudBlazor @using Microsoft.AspNetCore.SignalR.Client @using RobotNet10.RobotApp.Client.Clients @using RobotNet10.RobotApp.Client.Shared.Devices @implements IAsyncDisposable @inject ModbusTcpHubClient ModbusTcpHubClient @inject ISnackbar Snackbar @DeviceName @DeviceId IP: @ModbusData.IpAddress Port: @ModbusData.Port Slave ID: @ModbusData.SlaveId @(ModbusData.IsConnected ? "Connected" : "Disconnected") @if (ModbusData.HoldingRegisters != null && ModbusData.HoldingRegisters.Length > 0) { @foreach (var range in ModbusData.HoldingRegisters) { Holding Registers: @range.Name Address: @range.StartAddress - @(range.StartAddress + range.Quantity - 1) (@range.Quantity registers) Address Index Name Value (Decimal) Value (Hex) @context.Address @context.Index @if (!string.IsNullOrEmpty(context.Name)) { @context.Name } else { - } @context.Value 0x@(context.Value.ToString("X4")) } } @if (ModbusData.InputRegisters != null && ModbusData.InputRegisters.Length > 0) { @foreach (var range in ModbusData.InputRegisters) { Input Registers: @range.Name Address: @range.StartAddress - @(range.StartAddress + range.Quantity - 1) (@range.Quantity registers) Address Index Name Value (Decimal) Value (Hex) @context.Address @context.Index @if (!string.IsNullOrEmpty(context.Name)) { @context.Name } else { - } @context.Value 0x@(context.Value.ToString("X4")) } } @if (ModbusData.Coils != null && ModbusData.Coils.Length > 0) { @foreach (var range in ModbusData.Coils) { Coils: @range.Name Address: @range.StartAddress - @(range.StartAddress + range.Quantity - 1) (@range.Quantity coils) Address Index Name Value Action @context.Address @context.Index @if (!string.IsNullOrEmpty(context.Name)) { @context.Name } else { - } @(context.Value ? "ON" : "OFF") @{ var coilKey = $"{range.StartAddress}_{context.Index}"; var isWriting = WritingCoils.ContainsKey(coilKey) && WritingCoils[coilKey]; } @if (isWriting) { } } } @if (ModbusData.DiscreteInputs != null && ModbusData.DiscreteInputs.Length > 0) { @foreach (var range in ModbusData.DiscreteInputs) { Discrete Inputs: @range.Name Address: @range.StartAddress - @(range.StartAddress + range.Quantity - 1) (@range.Quantity inputs) Address Index Name Value @context.Address @context.Index @if (!string.IsNullOrEmpty(context.Name)) { @context.Name } else { - } @(context.Value ? "ON" : "OFF") } } @code { [Parameter, EditorRequired] public string DeviceId { get; set; } = string.Empty; private string DeviceName { get; set; } = string.Empty; private ModbusTcpData ModbusData = new(); private bool IsLoading => !ModbusTcpHubClient.IsConnected; private bool IsReloading = false; private Dictionary WritingCoils = new(); protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && !string.IsNullOrEmpty(DeviceId)) { await ConnectAsync(); } await base.OnAfterRenderAsync(firstRender); } private async Task ConnectAsync() { try { await ModbusTcpHubClient.StartAsync(); // Lấy DeviceName từ ModbusTcpHub var deviceInfo = await ModbusTcpHubClient.GetDeviceInfoAsync(DeviceId); if (deviceInfo != null) { DeviceName = deviceInfo.DeviceName; } else { DeviceName = DeviceId; // Fallback to DeviceId if not found } ModbusData = await ModbusTcpHubClient.GetModbusDataAsync(DeviceId); StateHasChanged(); } catch (Exception ex) { Snackbar.Add($"Failed to connect: {ex.Message}", Severity.Error); } } private async Task DisconnectAsync() { try { await ModbusTcpHubClient.StopAsync(); ModbusData = new ModbusTcpData(); // Reset về giá trị mặc định StateHasChanged(); } catch (Exception ex) { Snackbar.Add($"Failed to disconnect: {ex.Message}", Severity.Error); } } private async Task ReloadModbusDataAsync() { if (!ModbusTcpHubClient.IsConnected || IsReloading) return; try { IsReloading = true; StateHasChanged(); ModbusData = await ModbusTcpHubClient.GetModbusDataAsync(DeviceId); } catch (Exception ex) { Snackbar.Add($"Failed to reload Modbus data: {ex.Message}", Severity.Error); } finally { IsReloading = false; StateHasChanged(); } } private async Task HandleCoilToggle(ushort address, bool value, string coilKey) { if (!ModbusTcpHubClient.IsConnected) { Snackbar.Add("Not connected to Modbus device", Severity.Warning); return; } // Set writing state WritingCoils[coilKey] = true; StateHasChanged(); try { var success = await ModbusTcpHubClient.WriteCoilAsync(DeviceId, address, value); if (success) { Snackbar.Add($"Coil {address} set to {(value ? "ON" : "OFF")}", Severity.Success); // Reload data để cập nhật giá trị mới nhất await ReloadModbusDataAsync(); } else { Snackbar.Add($"Failed to write coil {address}", Severity.Error); // Reload để khôi phục giá trị cũ await ReloadModbusDataAsync(); } } catch (Exception ex) { Snackbar.Add($"Error writing coil {address}: {ex.Message}", Severity.Error); // Reload để khôi phục giá trị cũ await ReloadModbusDataAsync(); } finally { WritingCoils[coilKey] = false; StateHasChanged(); } } public async ValueTask DisposeAsync() { await DisconnectAsync(); } }