@using RobotNet10.RobotApp.Client.Clients @using RobotNet10.RobotApp.Client.Shared.Modules @using MudBlazor Lift Module @* Lift Status Information *@ @if (liftStatus != null) { Module Status State: @liftStatus.State Ready: @liftStatus.IsReady Current Position: @($"{liftStatus.CurrentPosition:N0} counts") (@($"{ConvertCountsToMeters(liftStatus.CurrentPosition):F3} m")) } @* Lift Control Buttons *@ Homing & Movement Controls Homing Up Down Stop Go Scale: 10,000 counts = 0.01 m (1,000,000 counts = 1 m) @code { [Parameter, EditorRequired] public MotionHubClient HubClient { get; set; } = null!; [Parameter] public bool IsHubReady { get; set; } [Inject] private ISnackbar Snackbar { get; set; } = null!; [Inject] private CiA402ServoHubClient ServoHubClient { get; set; } = null!; private LiftModuleStatusDto? liftStatus; private bool isLoading = false; private double targetHeightMeters = 0.0; private bool _previousIsHubReady = false; protected override async Task OnParametersSetAsync() { // Detect when IsHubReady changes from false to true if (IsHubReady && !_previousIsHubReady) { await RefreshStatus(); } _previousIsHubReady = IsHubReady; // Đảm bảo kết nối tới CiA402ServoHub để homing trực tiếp if (!ServoHubClient.IsConnected) { try { await ServoHubClient.StartAsync(); } catch { // Nếu connect lỗi, homing sẽ báo lỗi qua Snackbar } } } public async Task RefreshStatus() { if (isLoading || !IsHubReady) return; try { isLoading = true; liftStatus = await HubClient.GetLiftStatusAsync(); if (liftStatus != null) { targetHeightMeters = ConvertCountsToMeters(liftStatus.CurrentPosition); } StateHasChanged(); } catch (Exception ex) { Snackbar.Add($"Error refreshing lift status: {ex.Message}", Severity.Error); } finally { isLoading = false; } } private Color GetStateColor(string state) { return state switch { "Ready" => Color.Success, "Moving" => Color.Info, "Error" => Color.Error, "Homing" => Color.Warning, "Initializing" => Color.Warning, _ => Color.Default }; } private async Task LiftUp() { if (!IsHubReady || isLoading || liftStatus?.IsReady != true) return; try { isLoading = true; Snackbar.Add("Lifting up...", Severity.Info); await HubClient.LiftUpAsync(); Snackbar.Add("Lift up command sent successfully", Severity.Success); await Task.Delay(500); await RefreshStatus(); } catch (Exception ex) { Snackbar.Add($"Error lifting up: {ex.Message}", Severity.Error); } finally { isLoading = false; StateHasChanged(); } } private async Task LiftDown() { if (!IsHubReady || isLoading || liftStatus?.IsReady != true) return; try { isLoading = true; Snackbar.Add("Lifting down...", Severity.Info); await HubClient.LiftDownAsync(); Snackbar.Add("Lift down command sent successfully", Severity.Success); await Task.Delay(500); await RefreshStatus(); } catch (Exception ex) { Snackbar.Add($"Error lifting down: {ex.Message}", Severity.Error); } finally { isLoading = false; StateHasChanged(); } } private async Task LiftStop() { if (!IsHubReady || liftStatus?.IsReady != true) return; try { await HubClient.LiftStopAsync(); Snackbar.Add("Lift stop command sent", Severity.Info); await Task.Delay(300); await RefreshStatus(); } catch (Exception ex) { Snackbar.Add($"Error stopping lift: {ex.Message}", Severity.Error); } finally { StateHasChanged(); } } private async Task MoveToPosition() { if (!IsHubReady || isLoading || liftStatus?.IsReady != true) return; try { isLoading = true; var targetCounts = ConvertMetersToCounts(targetHeightMeters); Snackbar.Add($"Moving to height {targetHeightMeters:F3} m (~{targetCounts} counts)...", Severity.Info); await HubClient.LiftToPositionAsync(targetCounts); Snackbar.Add($"Move to height {targetHeightMeters:F3} m command sent successfully", Severity.Success); await Task.Delay(500); await RefreshStatus(); } catch (Exception ex) { Snackbar.Add($"Error moving to position: {ex.Message}", Severity.Error); } finally { isLoading = false; StateHasChanged(); } } private async Task LiftHome() { if (isLoading) return; try { isLoading = true; Snackbar.Add("Starting lift homing (direct device)...", Severity.Info); const string liftDeviceId = "lift-motor"; // Dùng cùng tham số như appsettings (hoặc theo nhu cầu của bạn) const byte homingMethod = 21; const int homingSpeed = 20000; const int homingOffset = 0; // Ghi homing params xuống drive giống CiA402ServoCard await ServoHubClient.SetHomingMethodAsync(liftDeviceId, homingMethod); await ServoHubClient.SetHomingSpeedAsync(liftDeviceId, homingSpeed); await ServoHubClient.SetHomingOffsetAsync(liftDeviceId, homingOffset); // Start homing trực tiếp trên thiết bị await ServoHubClient.StartHomingAsync(liftDeviceId, homingMethod, homingSpeed); Snackbar.Add("Lift homing command sent to lift-motor", Severity.Success); await Task.Delay(500); await RefreshStatus(); } catch (Exception ex) { Snackbar.Add($"Error homing lift: {ex.Message}", Severity.Error); } finally { isLoading = false; StateHasChanged(); } } private static double ConvertCountsToMeters(int counts) { // 10,000 counts = 0.01 m => 1,000,000 counts = 1 m return counts / 1_000_000.0; } private static int ConvertMetersToCounts(double meters) { return (int)Math.Round(meters * 1_000_000.0); } }