338 lines
12 KiB
Plaintext
338 lines
12 KiB
Plaintext
@using RobotNet10.RobotApp.Client.Clients
|
|
@using RobotNet10.RobotApp.Client.Shared.Modules
|
|
@using MudBlazor
|
|
|
|
<MudPaper Class="pa-4" Style="height: 100%;">
|
|
<MudStack Spacing="3">
|
|
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
|
<MudText Typo="Typo.h5">Lift Module</MudText>
|
|
<MudIconButton Icon="@Icons.Material.Filled.Refresh"
|
|
Color="Color.Primary"
|
|
Size="Size.Small"
|
|
OnClick="RefreshStatus"
|
|
Disabled="@(!IsHubReady || isLoading)" />
|
|
</MudStack>
|
|
|
|
@* Lift Status Information *@
|
|
@if (liftStatus != null)
|
|
{
|
|
<MudCard>
|
|
<MudCardContent>
|
|
<MudText Typo="Typo.h6" Class="mb-3">Module Status</MudText>
|
|
<MudStack Spacing="2">
|
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
|
<MudText>State:</MudText>
|
|
<MudChip T="string" Color="@GetStateColor(liftStatus.State)" Size="Size.Small">
|
|
@liftStatus.State
|
|
</MudChip>
|
|
</MudStack>
|
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
|
<MudText>Ready:</MudText>
|
|
<MudChip T="bool" Color="@(liftStatus.IsReady ? Color.Success : Color.Default)" Size="Size.Small">
|
|
@liftStatus.IsReady
|
|
</MudChip>
|
|
</MudStack>
|
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
|
<MudText>Current Position:</MudText>
|
|
<MudText>
|
|
@($"{liftStatus.CurrentPosition:N0} counts")
|
|
(@($"{ConvertCountsToMeters(liftStatus.CurrentPosition):F3} m"))
|
|
</MudText>
|
|
</MudStack>
|
|
</MudStack>
|
|
</MudCardContent>
|
|
</MudCard>
|
|
}
|
|
|
|
@* Lift Control Buttons *@
|
|
<MudStack Spacing="3">
|
|
<MudText Typo="Typo.h6">Homing & Movement Controls</MudText>
|
|
|
|
<MudStack Row="true" Spacing="3" AlignItems="AlignItems.Center">
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Primary"
|
|
OnClick="LiftHome"
|
|
Disabled="@(isLoading || !IsHubReady)">
|
|
<MudIcon Icon="@Icons.Material.Filled.HomeRepairService" Class="mr-2" />
|
|
<MudText>Homing</MudText>
|
|
</MudButton>
|
|
</MudStack>
|
|
|
|
<MudStack Row="true" Spacing="3" AlignItems="@AlignItems.Center">
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Success"
|
|
OnClick="LiftUp"
|
|
Disabled="@(isLoading || !IsHubReady || liftStatus?.IsReady != true)">
|
|
<MudIcon Icon="@Icons.Material.Filled.ArrowUpward" Class="mr-2" />
|
|
<MudText>Up</MudText>
|
|
</MudButton>
|
|
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Error"
|
|
OnClick="LiftDown"
|
|
Disabled="@(isLoading || !IsHubReady || liftStatus?.IsReady != true)">
|
|
<MudIcon Icon="@Icons.Material.Filled.ArrowDownward" Class="mr-2" />
|
|
<MudText>Down</MudText>
|
|
</MudButton>
|
|
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Warning"
|
|
OnClick="LiftStop"
|
|
Disabled="@(!IsHubReady || liftStatus?.IsReady != true || liftStatus?.State != "Moving")">
|
|
<MudIcon Icon="@Icons.Material.Filled.Stop" Class="mr-2" />
|
|
<MudText>Stop</MudText>
|
|
</MudButton>
|
|
</MudStack>
|
|
|
|
<MudStack Row="true" Spacing="3" AlignItems="@AlignItems.Center">
|
|
<MudNumericField @bind-Value="targetHeightMeters"
|
|
Label="Target Height (m)"
|
|
Variant="Variant.Outlined"
|
|
Min="0"
|
|
Class="flex-grow-1" />
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Primary"
|
|
OnClick="MoveToPosition"
|
|
Disabled="@(isLoading || !IsHubReady || liftStatus?.IsReady != true)">
|
|
<MudIcon Icon="@Icons.Material.Filled.LocationOn" Class="mr-2" />
|
|
<MudText>Go</MudText>
|
|
</MudButton>
|
|
</MudStack>
|
|
<MudText Typo="Typo.caption">
|
|
Scale: 10,000 counts = 0.01 m (1,000,000 counts = 1 m)
|
|
</MudText>
|
|
</MudStack>
|
|
</MudStack>
|
|
</MudPaper>
|
|
|
|
@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);
|
|
}
|
|
}
|