1095 lines
53 KiB
Plaintext
1095 lines
53 KiB
Plaintext
@implements IAsyncDisposable
|
|
|
|
@using MudBlazor
|
|
@using Microsoft.AspNetCore.SignalR.Client
|
|
@using RobotNet10.RobotApp.Client.Clients
|
|
@using RobotNet10.RobotApp.Client.Shared.Devices
|
|
|
|
@inject CiA402ServoHubClient CiA402ServoHubClient
|
|
@inject ISnackbar Snackbar
|
|
|
|
<MudCard>
|
|
<MudCardHeader>
|
|
<CardHeaderContent>
|
|
<MudText Typo="Typo.h5">@DeviceName</MudText>
|
|
<MudText Typo="Typo.body2" Color="Color.Secondary">@DeviceId</MudText>
|
|
</CardHeaderContent>
|
|
<CardHeaderActions>
|
|
<MudTooltip Text="Reload servo data">
|
|
<MudIconButton Icon="@Icons.Material.Filled.Refresh"
|
|
Color="Color.Primary"
|
|
Size="Size.Small"
|
|
OnClick="ReloadServoDataAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsReloading)">
|
|
</MudIconButton>
|
|
</MudTooltip>
|
|
</CardHeaderActions>
|
|
</MudCardHeader>
|
|
<MudCardContent Style="position: relative;" Class="pa-2">
|
|
<MudGrid Spacing="2">
|
|
<!-- Status & Connection -->
|
|
<MudItem xs="12">
|
|
<MudCard Elevation="0" Class="pa-2">
|
|
<MudGrid Spacing="1">
|
|
<MudItem xs="12" sm="3">
|
|
<MudChip T="string" Color="@(ServoData.IsConnected ? Color.Success : Color.Error)"
|
|
Size="Size.Small"
|
|
Variant="Variant.Filled">
|
|
@(ServoData.IsConnected ? "Connected" : "Disconnected")
|
|
</MudChip>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="3">
|
|
<MudText Typo="Typo.body2">
|
|
<strong>State:</strong>
|
|
<MudChip T="string" Size="Size.Small" Variant="Variant.Text" Color="Color.Info">
|
|
@ServoData.DriveState
|
|
</MudChip>
|
|
</MudText>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="3">
|
|
<MudText Typo="Typo.body2">
|
|
<strong>Mode:</strong>
|
|
<MudChip T="string" Size="Size.Small" Variant="Variant.Text" Color="Color.Success">
|
|
@ServoData.OperationMode
|
|
</MudChip>
|
|
</MudText>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="3">
|
|
<MudText Typo="Typo.body2">
|
|
<strong>Error:</strong>
|
|
<MudChip T="string" Size="Size.Small"
|
|
Variant="Variant.Text"
|
|
Color="@(ServoData.ErrorCode == 0 ? Color.Success : Color.Error)">
|
|
0x@(ServoData.ErrorCode.ToString("X4"))
|
|
</MudChip>
|
|
</MudText>
|
|
</MudItem>
|
|
<MudItem xs="12">
|
|
<MudText Typo="Typo.body2">
|
|
<strong>Statusword:</strong> 0x@(ServoData.Statusword.ToString("X4"))
|
|
</MudText>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudCard>
|
|
</MudItem>
|
|
|
|
<!-- Position, Velocity, Torque -->
|
|
<MudItem xs="12">
|
|
<MudCard Elevation="0" Class="pa-2">
|
|
<MudGrid Spacing="1">
|
|
<MudItem xs="12" sm="4">
|
|
<MudText Typo="Typo.body2">
|
|
<strong>Position:</strong>
|
|
</MudText>
|
|
<MudText Typo="Typo.body1" Color="Color.Primary">
|
|
@ServoData.Position.ToString("N0")
|
|
</MudText>
|
|
<MudText Typo="Typo.caption" Color="Color.Secondary">counts</MudText>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudText Typo="Typo.body2">
|
|
<strong>Velocity:</strong>
|
|
</MudText>
|
|
<MudText Typo="Typo.body1" Color="@GetVelocityColor()">
|
|
@ServoData.Velocity.ToString("N0")
|
|
</MudText>
|
|
<MudText Typo="Typo.caption" Color="Color.Secondary">counts/s</MudText>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudText Typo="Typo.body2">
|
|
<strong>Torque:</strong>
|
|
</MudText>
|
|
<MudText Typo="Typo.body1" Color="Color.Warning">
|
|
@ServoData.Torque.ToString("N0")
|
|
</MudText>
|
|
<MudText Typo="Typo.caption" Color="Color.Secondary">units</MudText>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudCard>
|
|
</MudItem>
|
|
|
|
<!-- Controls Section -->
|
|
<MudItem xs="12">
|
|
<MudCard Elevation="0" Class="pa-2">
|
|
<!-- State Machine Controls -->
|
|
<MudText Typo="Typo.subtitle2" Color="Color.Primary" Class="mb-1">State Control</MudText>
|
|
<MudButtonGroup OverrideStyles="false" DropShadow>
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Success"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.PlayArrow"
|
|
OnClick="HandleEnableAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Enable
|
|
</MudButton>
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Error"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.Stop"
|
|
OnClick="HandleDisableAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Disable
|
|
</MudButton>
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Warning"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.Emergency"
|
|
OnClick="HandleQuickStopAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Quick Stop
|
|
</MudButton>
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Info"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.Refresh"
|
|
OnClick="HandleFaultResetAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Reset
|
|
</MudButton>
|
|
</MudButtonGroup>
|
|
|
|
<!-- Advanced State Machine Controls -->
|
|
<MudExpansionPanels Elevation="0" Class="mt-2">
|
|
<MudExpansionPanel Text="Advanced State Control" Icon="@Icons.Material.Filled.Settings">
|
|
<MudGrid Spacing="1">
|
|
<MudItem xs="6" sm="3">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Primary"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.PowerOff"
|
|
OnClick="HandleShutdownAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Shutdown
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="6" sm="3">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Primary"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.Power"
|
|
OnClick="HandleSwitchOnAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Switch On
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="6" sm="3">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Success"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.PlayCircle"
|
|
OnClick="HandleEnableOperationAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Enable Op
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="6" sm="3">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Error"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.PauseCircle"
|
|
OnClick="HandleDisableOperationAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Disable Op
|
|
</MudButton>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudExpansionPanel>
|
|
</MudExpansionPanels>
|
|
|
|
<!-- Operation Mode -->
|
|
<MudStack Row AlignItems="@AlignItems.Center" Class="mt-2">
|
|
<MudSelect T="string"
|
|
Label="Operation Mode"
|
|
ShrinkLabel
|
|
Dense Margin="@Margin.Dense"
|
|
Variant="Variant.Outlined"
|
|
Value="@SelectedOperationMode"
|
|
ValueChanged="OnOperationModeChanged"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
<MudSelectItem Value="@("ProfilePosition")">Profile Position</MudSelectItem>
|
|
<MudSelectItem Value="@("ProfileVelocity")">Profile Velocity</MudSelectItem>
|
|
<MudSelectItem Value="@("ProfileTorque")">Profile Torque</MudSelectItem>
|
|
<MudSelectItem Value="@("Homing")">Homing</MudSelectItem>
|
|
<MudSelectItem Value="@("NoMode")">No Mode</MudSelectItem>
|
|
</MudSelect>
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Primary"
|
|
Size="Size.Small"
|
|
OnClick="HandleSetOperationModeAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting || string.IsNullOrEmpty(SelectedOperationMode))">
|
|
Set
|
|
</MudButton>
|
|
</MudStack>
|
|
|
|
<!-- Position Control -->
|
|
<MudText Typo="Typo.subtitle2" Color="Color.Primary" Class="mb-1 mt-2">Position Control</MudText>
|
|
<MudGrid Spacing="1" Class="mb-2">
|
|
<MudItem xs="12" sm="6">
|
|
<MudNumericField T="int"
|
|
Label="Target Position"
|
|
Variant="Variant.Outlined"
|
|
Margin="Margin.Dense"
|
|
@bind-Value="@TargetPosition"
|
|
HelperText="counts (can be negative)"
|
|
Min="@int.MinValue"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)" />
|
|
</MudItem>
|
|
<MudItem xs="6" sm="2">
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Primary"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.LocationOn"
|
|
OnClick="HandleSetTargetPositionAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Set
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="6" sm="2">
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Success"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.Navigation"
|
|
OnClick="HandleMoveToPositionAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Move
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="6" sm="2">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Info"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.PlayArrow"
|
|
OnClick="HandleStartPositionMoveAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Start
|
|
</MudButton>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<!-- Wait Until At Target -->
|
|
<MudGrid Spacing="1" Class="mb-2">
|
|
<MudItem xs="12" sm="6">
|
|
<MudNumericField T="int"
|
|
Label="Tolerance"
|
|
Variant="Variant.Outlined"
|
|
Margin="Margin.Dense"
|
|
@bind-Value="@PositionTolerance"
|
|
HelperText="counts (can be negative)"
|
|
Min="@int.MinValue"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)" />
|
|
</MudItem>
|
|
<MudItem xs="12" sm="3">
|
|
<MudCheckBox T="bool"
|
|
Label="Use Statusword"
|
|
@bind-Value="@UseStatuswordForTarget"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)" />
|
|
</MudItem>
|
|
<MudItem xs="12" sm="3">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Success"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.HourglassEmpty"
|
|
OnClick="HandleWaitUntilAtTargetAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Wait Target
|
|
</MudButton>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<!-- Profile Settings for Position -->
|
|
<MudExpansionPanels Elevation="0" Class="mb-2">
|
|
<MudExpansionPanel Text="Profile Settings" Icon="@Icons.Material.Filled.Tune">
|
|
<MudGrid Spacing="1">
|
|
<MudItem xs="12" sm="4">
|
|
<MudNumericField T="uint"
|
|
Label="Profile Speed"
|
|
Variant="Variant.Outlined"
|
|
Margin="Margin.Dense"
|
|
@bind-Value="@ProfileSpeed"
|
|
HelperText="counts/s"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)" />
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudNumericField T="uint"
|
|
Label="Acceleration"
|
|
Variant="Variant.Outlined"
|
|
Margin="Margin.Dense"
|
|
@bind-Value="@ProfileAcceleration"
|
|
HelperText="counts/s²"
|
|
Min="0"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)" />
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudNumericField T="uint"
|
|
Label="Deceleration"
|
|
Variant="Variant.Outlined"
|
|
Margin="Margin.Dense"
|
|
@bind-Value="@ProfileDeceleration"
|
|
HelperText="counts/s²"
|
|
Min="0"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)" />
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Primary"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
OnClick="HandleSetProfileSpeedAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Set Speed
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Primary"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
OnClick="HandleSetProfileAccelerationAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Set Accel
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Primary"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
OnClick="HandleSetProfileDecelerationAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Set Decel
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="12">
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Primary"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
OnClick="HandleSetAllProfileAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Set All Profile (write Speed + Accel + Decel to drive)
|
|
</MudButton>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudExpansionPanel>
|
|
</MudExpansionPanels>
|
|
|
|
<!-- Velocity & Torque Controls -->
|
|
<MudGrid Spacing="1">
|
|
<MudItem xs="12" sm="6">
|
|
<MudText Typo="Typo.subtitle2" Color="Color.Info" Class="mb-1">Velocity</MudText>
|
|
<MudGrid Spacing="1">
|
|
<MudItem xs="12" sm="8">
|
|
<MudNumericField T="int"
|
|
Label="Target Velocity"
|
|
Variant="Variant.Outlined"
|
|
Margin="Margin.Dense"
|
|
@bind-Value="@TargetVelocity"
|
|
HelperText="counts/s (can be negative)"
|
|
Min="@int.MinValue"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)" />
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Info"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.Speed"
|
|
OnClick="HandleSetTargetVelocityAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Set
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Info"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.PlayArrow"
|
|
OnClick="HandleTargetVelocityAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Target Vel
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Info"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.Speed"
|
|
OnClick="HandleProfileVelocityAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Profile Vel
|
|
</MudButton>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6">
|
|
<MudText Typo="Typo.subtitle2" Color="Color.Warning" Class="mb-1">Torque</MudText>
|
|
<MudGrid Spacing="1">
|
|
<MudItem xs="12" sm="8">
|
|
<MudNumericField T="short"
|
|
Label="Target Torque"
|
|
Variant="Variant.Outlined"
|
|
Margin="Margin.Dense"
|
|
@bind-Value="@TargetTorque"
|
|
HelperText="units (can be negative)"
|
|
Min="@short.MinValue"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)" />
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Warning"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.Power"
|
|
OnClick="HandleSetTargetTorqueAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Set
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="12">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Warning"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.PlayArrow"
|
|
OnClick="HandleRunTorqueAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Run Torque
|
|
</MudButton>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<!-- Homing Control -->
|
|
<MudText Typo="Typo.subtitle2" Color="Color.Secondary" Class="mb-1 mt-2">Homing</MudText>
|
|
<MudGrid Spacing="1" Class="mb-2">
|
|
<MudItem xs="12" sm="4">
|
|
<MudNumericField T="byte"
|
|
Label="Homing Method"
|
|
Variant="Variant.Outlined"
|
|
Margin="Margin.Dense"
|
|
@bind-Value="@HomingMethod"
|
|
HelperText="Method number "
|
|
Min="1"
|
|
Max="254"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)" />
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudNumericField T="int"
|
|
Label="Homing Speed"
|
|
Variant="Variant.Outlined"
|
|
Margin="Margin.Dense"
|
|
@bind-Value="@HomingSpeed"
|
|
HelperText="counts/s (can be negative)"
|
|
Min="@int.MinValue"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)" />
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudNumericField T="int"
|
|
Label="Homing Offset"
|
|
Variant="Variant.Outlined"
|
|
Margin="Margin.Dense"
|
|
@bind-Value="@HomingOffset"
|
|
HelperText="counts (can be negative)"
|
|
Min="@int.MinValue"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)" />
|
|
</MudItem>
|
|
<MudItem xs="6" sm="6">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Secondary"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
OnClick="HandleApplyHomingParamsAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Apply Params
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="6" sm="6">
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Secondary"
|
|
Size="Size.Small"
|
|
FullWidth="true"
|
|
StartIcon="@Icons.Material.Filled.Home"
|
|
OnClick="HandleStartHomingAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Start
|
|
</MudButton>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<!-- Error Information -->
|
|
<MudExpansionPanels Elevation="0" Class="mb-2">
|
|
<MudExpansionPanel Text="Error Information" Icon="@Icons.Material.Filled.Error">
|
|
<MudGrid Spacing="1">
|
|
<MudItem xs="12">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Error"
|
|
Size="Size.Small"
|
|
StartIcon="@Icons.Material.Filled.Refresh"
|
|
OnClick="HandleRefreshErrorInfoAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Refresh Error Info
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6">
|
|
<MudText Typo="Typo.body2">
|
|
<strong>Error Register:</strong> 0x@(ErrorRegister.ToString("X2"))
|
|
</MudText>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6">
|
|
<MudText Typo="Typo.body2">
|
|
<strong>Latest Error:</strong> 0x@(LatestErrorCode.ToString("X4"))
|
|
</MudText>
|
|
</MudItem>
|
|
<MudItem xs="12">
|
|
<MudText Typo="Typo.body2">
|
|
<strong>Error History:</strong>
|
|
</MudText>
|
|
@if (ErrorHistory != null && ErrorHistory.Length > 0)
|
|
{
|
|
<MudChipSet T="ushort">
|
|
@foreach (var error in ErrorHistory)
|
|
{
|
|
<MudChip T="ushort"
|
|
Color="Color.Error"
|
|
Size="Size.Small"
|
|
Variant="Variant.Text">
|
|
0x@(error.ToString("X4"))
|
|
</MudChip>
|
|
}
|
|
</MudChipSet>
|
|
}
|
|
else
|
|
{
|
|
<MudText Typo="Typo.caption" Color="Color.Secondary">No errors</MudText>
|
|
}
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudExpansionPanel>
|
|
</MudExpansionPanels>
|
|
|
|
<!-- Status Information -->
|
|
<MudExpansionPanels Elevation="0" Class="mb-2">
|
|
<MudExpansionPanel Text="Status Information" Icon="@Icons.Material.Filled.Info">
|
|
<MudGrid Spacing="1">
|
|
<MudItem xs="12">
|
|
<MudButton Variant="Variant.Outlined"
|
|
Color="Color.Info"
|
|
Size="Size.Small"
|
|
StartIcon="@Icons.Material.Filled.Refresh"
|
|
OnClick="HandleRefreshStatusInfoAsync"
|
|
Disabled="@(!CiA402ServoHubClient.IsConnected || IsActionExecuting)">
|
|
Refresh Status
|
|
</MudButton>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudChip T="bool"
|
|
Color="@(IsInFault ? Color.Error : Color.Success)"
|
|
Size="Size.Small"
|
|
Variant="Variant.Filled">
|
|
@(IsInFault ? "In Fault" : "No Fault")
|
|
</MudChip>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudChip T="bool"
|
|
Color="@(IsEnabled ? Color.Success : Color.Default)"
|
|
Size="Size.Small"
|
|
Variant="Variant.Filled">
|
|
@(IsEnabled ? "Enabled" : "Disabled")
|
|
</MudChip>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="4">
|
|
<MudChip T="bool"
|
|
Color="@(IsReady ? Color.Success : Color.Default)"
|
|
Size="Size.Small"
|
|
Variant="Variant.Filled">
|
|
@(IsReady ? "Ready" : "Not Ready")
|
|
</MudChip>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudExpansionPanel>
|
|
</MudExpansionPanels>
|
|
</MudCard>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudCardContent>
|
|
</MudCard>
|
|
|
|
<MudOverlay Visible="@IsLoading" Absolute="true">
|
|
<MudProgressCircular Indeterminate="true" Size="Size.Large" />
|
|
</MudOverlay>
|
|
|
|
@code {
|
|
[Parameter, EditorRequired]
|
|
public string DeviceId { get; set; } = string.Empty;
|
|
|
|
private string DeviceName { get; set; } = string.Empty;
|
|
private CiA402ServoDataDto ServoData = new();
|
|
private bool IsLoading => !CiA402ServoHubClient.IsConnected;
|
|
private bool IsReloading = false;
|
|
private bool IsActionExecuting = false;
|
|
|
|
// Control values
|
|
private string SelectedOperationMode { get; set; } = string.Empty;
|
|
private int TargetPosition { get; set; } = 0;
|
|
private int TargetVelocity { get; set; } = 0;
|
|
private short TargetTorque { get; set; } = 0;
|
|
|
|
// Profile Settings
|
|
private uint ProfileSpeed { get; set; } = 5000;
|
|
private uint ProfileAcceleration { get; set; } = 5000;
|
|
private uint ProfileDeceleration { get; set; } = 5000;
|
|
|
|
// Homing
|
|
private byte HomingMethod { get; set; } = 21;
|
|
private int HomingSpeed { get; set; } = 5000;
|
|
private int HomingOffset { get; set; } = 0;
|
|
|
|
// Error Information
|
|
private byte ErrorRegister { get; set; } = 0;
|
|
private ushort LatestErrorCode { get; set; } = 0;
|
|
private ushort[] ErrorHistory { get; set; } = [];
|
|
|
|
// Status Information
|
|
private bool IsInFault { get; set; } = false;
|
|
private bool IsEnabled { get; set; } = false;
|
|
private bool IsReady { get; set; } = false;
|
|
|
|
// Wait Until At Target
|
|
private int PositionTolerance { get; set; } = 100;
|
|
private bool UseStatuswordForTarget { get; set; } = true;
|
|
|
|
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 CiA402ServoHubClient.StartAsync();
|
|
|
|
var deviceInfo = await CiA402ServoHubClient.GetDeviceInfoAsync(DeviceId);
|
|
if (deviceInfo != null)
|
|
{
|
|
DeviceName = deviceInfo.DeviceName;
|
|
}
|
|
else
|
|
{
|
|
DeviceName = DeviceId;
|
|
}
|
|
|
|
ServoData = await CiA402ServoHubClient.GetServoDataAsync(DeviceId);
|
|
SyncProfileFromServoData();
|
|
StateHasChanged();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"Failed to connect: {ex.Message}", Severity.Error);
|
|
}
|
|
}
|
|
|
|
private async Task DisconnectAsync()
|
|
{
|
|
try
|
|
{
|
|
await CiA402ServoHubClient.StopAsync();
|
|
ServoData = new CiA402ServoDataDto();
|
|
StateHasChanged();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"Failed to disconnect: {ex.Message}", Severity.Error);
|
|
}
|
|
}
|
|
|
|
private async Task ReloadServoDataAsync()
|
|
{
|
|
if (!CiA402ServoHubClient.IsConnected || IsReloading)
|
|
return;
|
|
|
|
try
|
|
{
|
|
IsReloading = true;
|
|
StateHasChanged();
|
|
|
|
ServoData = await CiA402ServoHubClient.GetServoDataAsync(DeviceId);
|
|
SyncProfileFromServoData();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"Failed to reload servo data: {ex.Message}", Severity.Error);
|
|
}
|
|
finally
|
|
{
|
|
IsReloading = false;
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
private void SyncProfileFromServoData()
|
|
{
|
|
ProfileSpeed = ServoData.ProfileSpeed;
|
|
ProfileAcceleration = ServoData.ProfileAcceleration;
|
|
ProfileDeceleration = ServoData.ProfileDeceleration;
|
|
}
|
|
|
|
private Color GetVelocityColor()
|
|
{
|
|
var absVelocity = Math.Abs(ServoData.Velocity);
|
|
if (absVelocity < 1)
|
|
{
|
|
return Color.Default;
|
|
}
|
|
else if (absVelocity < 100)
|
|
{
|
|
return Color.Info;
|
|
}
|
|
else if (absVelocity < 1000)
|
|
{
|
|
return Color.Success;
|
|
}
|
|
else
|
|
{
|
|
return Color.Warning;
|
|
}
|
|
}
|
|
|
|
// State Machine Control Handlers
|
|
private async Task HandleEnableAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.EnableAsync(DeviceId);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
private async Task HandleDisableAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.DisableAsync(DeviceId);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
private async Task HandleQuickStopAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.QuickStopAsync(DeviceId);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
private async Task HandleFaultResetAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.FaultResetAsync(DeviceId);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
// Operation Mode Handler
|
|
private void OnOperationModeChanged(string? value)
|
|
{
|
|
SelectedOperationMode = value ?? string.Empty;
|
|
}
|
|
|
|
private async Task HandleSetOperationModeAsync()
|
|
{
|
|
if (string.IsNullOrEmpty(SelectedOperationMode))
|
|
return;
|
|
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.SetOperationModeAsync(DeviceId, SelectedOperationMode);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
// Position Control Handlers
|
|
private async Task HandleSetTargetPositionAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.SetTargetPositionAsync(DeviceId, TargetPosition);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
private async Task HandleMoveToPositionAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.MoveToPositionAsync(DeviceId, TargetPosition);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
// Velocity Control Handler
|
|
private async Task HandleSetTargetVelocityAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.SetTargetVelocityAsync(DeviceId, TargetVelocity);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
// Torque Control Handler
|
|
private async Task HandleSetTargetTorqueAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.SetTargetTorqueAsync(DeviceId, TargetTorque);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
private async Task HandleRunTorqueAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.RunTorqueAsync(DeviceId, TargetTorque);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
// Velocity Control - Additional Methods
|
|
private async Task HandleTargetVelocityAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.TargetVelocityAsync(DeviceId, TargetVelocity, ProfileAcceleration, ProfileDeceleration);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
private async Task HandleProfileVelocityAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.ProfileVelocityAsync(DeviceId, TargetVelocity, ProfileAcceleration, ProfileDeceleration);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
// Profile Settings Handlers
|
|
private async Task HandleSetProfileSpeedAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.SetProfileSpeedAsync(DeviceId, ProfileSpeed);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
private async Task HandleSetProfileAccelerationAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.SetProfileAccelerationAsync(DeviceId, ProfileAcceleration);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
private async Task HandleSetProfileDecelerationAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.SetProfileDecelerationAsync(DeviceId, ProfileDeceleration);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
private async Task HandleSetAllProfileAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.SetProfileSettingsAsync(DeviceId, ProfileSpeed, ProfileAcceleration, ProfileDeceleration);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
// Position Control - Additional Methods
|
|
private async Task HandleStartPositionMoveAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.StartPositionMoveAsync(DeviceId);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
// Homing Handlers
|
|
private async Task HandleApplyHomingParamsAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.SetHomingMethodAsync(DeviceId, HomingMethod);
|
|
await CiA402ServoHubClient.SetHomingSpeedAsync(DeviceId, HomingSpeed);
|
|
await CiA402ServoHubClient.SetHomingOffsetAsync(DeviceId, HomingOffset);
|
|
await ReloadServoDataAsync();
|
|
// Read back from drive to verify settings were applied
|
|
var readMethod = await CiA402ServoHubClient.GetHomingMethodAsync(DeviceId);
|
|
var readSpeed = await CiA402ServoHubClient.GetHomingSpeedAsync(DeviceId);
|
|
var readOffset = await CiA402ServoHubClient.GetHomingOffsetAsync(DeviceId);
|
|
Snackbar.Add($"Applied to drive — Method: {readMethod}, Speed: {readSpeed}, Offset: {readOffset}", Severity.Success);
|
|
});
|
|
}
|
|
|
|
private async Task HandleStartHomingAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.SetHomingMethodAsync(DeviceId, HomingMethod);
|
|
await CiA402ServoHubClient.SetHomingSpeedAsync(DeviceId, HomingSpeed);
|
|
await CiA402ServoHubClient.SetHomingOffsetAsync(DeviceId, HomingOffset);
|
|
await CiA402ServoHubClient.StartHomingAsync(DeviceId, HomingMethod, HomingSpeed);
|
|
await ReloadServoDataAsync();
|
|
// Read back from drive to verify settings were applied
|
|
var readMethod = await CiA402ServoHubClient.GetHomingMethodAsync(DeviceId);
|
|
var readSpeed = await CiA402ServoHubClient.GetHomingSpeedAsync(DeviceId);
|
|
var readOffset = await CiA402ServoHubClient.GetHomingOffsetAsync(DeviceId);
|
|
Snackbar.Add($"Homing started. Drive values — Method: {readMethod}, Speed: {readSpeed}, Offset: {readOffset}", Severity.Success);
|
|
});
|
|
}
|
|
|
|
// Advanced State Machine Control Handlers
|
|
private async Task HandleShutdownAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.ShutdownAsync(DeviceId);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
private async Task HandleSwitchOnAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.SwitchOnAsync(DeviceId);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
private async Task HandleEnableOperationAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.EnableOperationAsync(DeviceId);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
private async Task HandleDisableOperationAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
await CiA402ServoHubClient.DisableOperationAsync(DeviceId);
|
|
await ReloadServoDataAsync();
|
|
});
|
|
}
|
|
|
|
// Error Information Handlers
|
|
private async Task HandleRefreshErrorInfoAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
ErrorRegister = await CiA402ServoHubClient.GetErrorRegisterAsync(DeviceId);
|
|
LatestErrorCode = await CiA402ServoHubClient.GetLatestErrorCodeAsync(DeviceId);
|
|
ErrorHistory = await CiA402ServoHubClient.GetErrorHistoryAsync(DeviceId);
|
|
StateHasChanged();
|
|
});
|
|
}
|
|
|
|
// Status Information Handlers
|
|
private async Task HandleRefreshStatusInfoAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
IsInFault = await CiA402ServoHubClient.IsInFaultStateAsync(DeviceId);
|
|
IsEnabled = await CiA402ServoHubClient.IsEnabledAsync(DeviceId);
|
|
IsReady = await CiA402ServoHubClient.IsReadyAsync(DeviceId);
|
|
StateHasChanged();
|
|
});
|
|
}
|
|
|
|
// Wait Until At Target Handler
|
|
private async Task HandleWaitUntilAtTargetAsync()
|
|
{
|
|
await ExecuteActionAsync(async () =>
|
|
{
|
|
try
|
|
{
|
|
Snackbar.Add("Waiting for servo to reach target position...", Severity.Info);
|
|
await CiA402ServoHubClient.WaitUntilAtTargetAsync(DeviceId, PositionTolerance, UseStatuswordForTarget);
|
|
Snackbar.Add("Servo reached target position!", Severity.Success);
|
|
await ReloadServoDataAsync();
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
Snackbar.Add("Wait for target was cancelled", Severity.Warning);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Helper method để execute action với error handling
|
|
private async Task ExecuteActionAsync(Func<Task> action)
|
|
{
|
|
if (!CiA402ServoHubClient.IsConnected || IsActionExecuting)
|
|
return;
|
|
|
|
try
|
|
{
|
|
IsActionExecuting = true;
|
|
StateHasChanged();
|
|
|
|
await action();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"Action failed: {ex.Message}", Severity.Error);
|
|
}
|
|
finally
|
|
{
|
|
IsActionExecuting = false;
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
await DisconnectAsync();
|
|
}
|
|
}
|
|
|