Skip to content

Commit

Permalink
feature(control schemes): display current port tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
nvsukhanov committed Aug 22, 2023
1 parent 0e442b2 commit 6a9a0de
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ export class ControlSchemeViewIoListComponent {
this.treeControl.dataNodes = this.dataSource.data;
if (!this.initialExpansionDone) {
this.initialExpansionDone = true;
this.treeControl.expandAll();
this.treeControl.dataNodes
.filter((node) => node.initiallyExpanded)
.forEach((node) => {
this.treeControl.expand(node);
});
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
[ioType]="node.ioType"
[isConnected]="node.isConnected"
></app-io-inline-view>

<ng-container *ngIf="node.runningTask">
<mat-icon [fontIcon]="'sync'"></mat-icon>
{{ node.runningTask | portCommandTaskSummary | ngrxPush }}
</ng-container>

<ng-container *ngIf="node.lastExecutedTask && !node.runningTask">
<mat-icon [fontIcon]="'check_circle'"></mat-icon>
{{ node.lastExecutedTask | portCommandTaskSummary | ngrxPush }}
</ng-container>

<a *ngIf="node.useAccelerationProfile || node.useDecelerationProfile"
class="mat-mdc-button port-config"
[routerLink]="portConfigRoute"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { NgIf } from '@angular/common';
import { MatIconModule } from '@angular/material/icon';
import { TranslocoModule } from '@ngneat/transloco';
import { RouterLink } from '@angular/router';
import { PushPipe } from '@ngrx/component';
import { IoInlineViewComponent } from '@app/shared';

import { ControlSchemeViewIoTreeNode } from '../../types';
import { RoutesBuilderService } from '../../../../routing';
import { PortCommandTaskSummaryPipe } from '../../../port-command-task-summary';

@Component({
standalone: true,
Expand All @@ -18,7 +20,9 @@ import { RoutesBuilderService } from '../../../../routing';
IoInlineViewComponent,
MatIconModule,
TranslocoModule,
RouterLink
RouterLink,
PortCommandTaskSummaryPipe,
PushPipe
],
changeDetection: ChangeDetectionStrategy.OnPush
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import {
HubModel,
HubStatsModel,
PORT_TASKS_SELECTORS,
PortTasksModel,
ROUTER_SELECTORS,
attachedIoModesIdFn,
attachedIoPortModeInfoIdFn,
attachedIosIdFn
attachedIosIdFn,
hubPortTasksIdFn
} from '@app/store';

import { ioHasMatchingModeForOpMode } from '../io-has-matching-mode-for-op-mode';
Expand All @@ -44,7 +46,8 @@ function createHubTreeNode(
hasCommunication: hubStats?.hasCommunication ?? false,
nodeType: ControlSchemeNodeTypes.Hub,
isConnected: !!hubStats,
children: []
children: [],
initiallyExpanded: true
};
}

Expand All @@ -57,6 +60,7 @@ function createIoTreeNode(
bindings: ControlSchemeBinding[],
portConfigs: ControlSchemePortConfig[],
controlSchemeId: string,
portTasksModelDictionary: Dictionary<PortTasksModel>
): ControlSchemeViewIoTreeNode {
const ioId = attachedIosIdFn({ hubId: hubConfig.hubId, portId });
const io = iosEntities[ioId];
Expand All @@ -66,6 +70,9 @@ function createIoTreeNode(
const useDecelerationProfile = bindings.filter((b) => b.useDecelerationProfile && b.portId === portId && b.hubId === hubConfig.hubId)
.length > 0;

const runningTask = portTasksModelDictionary[hubPortTasksIdFn({ hubId: hubConfig.hubId, portId })]?.runningTask ?? undefined;
const lastExecutedTask = portTasksModelDictionary[hubPortTasksIdFn({ hubId: hubConfig.hubId, portId })]?.lastExecutedTask ?? undefined;

return {
path: `${parentPath}.${portId}`,
nodeType: ControlSchemeNodeTypes.Io,
Expand All @@ -78,7 +85,10 @@ function createIoTreeNode(
accelerationTimeMs: portConfig?.accelerationTimeMs ?? 0,
useDecelerationProfile,
decelerationTimeMs: portConfig?.decelerationTimeMs ?? 0,
children: []
runningTask,
lastExecutedTask,
children: [],
initiallyExpanded: false
};
}

Expand All @@ -100,7 +110,8 @@ function createBindingTreeNode(
binding,
controlSchemeId,
ioHasNoRequiredCapabilities,
children: []
children: [],
initiallyExpanded: false
};
}

Expand All @@ -113,14 +124,16 @@ export const CONTROL_SCHEME_VIEW_SELECTORS = {
ATTACHED_IO_MODES_SELECTORS.selectEntities,
ATTACHED_IO_PORT_MODE_INFO_SELECTORS.selectEntities,
PORT_TASKS_SELECTORS.selectLastExecutedBindingIds,
PORT_TASKS_SELECTORS.selectEntities,
(
scheme: ControlSchemeModel | undefined,
hubEntities: Dictionary<HubModel>,
statsEntities: Dictionary<HubStatsModel>,
iosEntities: Dictionary<AttachedIoModel>,
ioSupportedModesEntities: Dictionary<AttachedIoModesModel>,
portModeInfoEntities: Dictionary<AttachedIoPortModeInfoModel>,
lastExecutedTasksBindingIds: ReadonlySet<string>
lastExecutedTasksBindingIds: ReadonlySet<string>,
portCommandTasksEntities: Dictionary<PortTasksModel>,
): ControlSchemeViewHubTreeNode[] => {
if (!scheme) {
return [];
Expand Down Expand Up @@ -178,7 +191,8 @@ export const CONTROL_SCHEME_VIEW_SELECTORS = {
binding.portId,
scheme.bindings,
scheme.portConfigs,
schemeId
schemeId,
portCommandTasksEntities
);
hubIosViewMap.set(ioId, ioViewModel);

Expand Down
7 changes: 6 additions & 1 deletion src/app/control-schemes/control-scheme-page/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { HubType, IOType } from '@nvsukhanov/rxpoweredup';
import { ControlSchemeBinding } from '@app/store';
import { ControlSchemeBinding, PortCommandTask } from '@app/store';

export enum ControlSchemeNodeTypes {
Hub = 'Hub',
Expand All @@ -15,6 +15,7 @@ export type ControlSchemeViewBindingTreeNodeData = {
readonly binding: ControlSchemeBinding;
readonly ioHasNoRequiredCapabilities: boolean;
readonly children: [];
readonly initiallyExpanded: boolean;
};

export type ControlSchemeViewIoTreeNode = {
Expand All @@ -29,7 +30,10 @@ export type ControlSchemeViewIoTreeNode = {
readonly accelerationTimeMs: number;
readonly useDecelerationProfile: boolean;
readonly decelerationTimeMs: number;
readonly runningTask?: PortCommandTask;
readonly lastExecutedTask?: PortCommandTask;
readonly children: ControlSchemeViewBindingTreeNodeData[];
readonly initiallyExpanded: boolean;
};

export type ControlSchemeViewHubTreeNode = {
Expand All @@ -44,6 +48,7 @@ export type ControlSchemeViewHubTreeNode = {
readonly hasCommunication: boolean; // TODO: remove, may impact performance, Use ad-hoc selector instead
readonly isConnected: boolean;
readonly children: ControlSchemeViewIoTreeNode[];
readonly initiallyExpanded: boolean;
};

export type ControlSchemeViewTreeNode = ControlSchemeViewHubTreeNode
Expand Down
1 change: 1 addition & 0 deletions src/app/control-schemes/port-command-task-summary/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './port-command-task-summary.pipe';
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { TranslocoService } from '@ngneat/transloco';
import { SetLinearSpeedTaskPayload } from '@app/store';

@Injectable({ providedIn: 'root' })
export class LinearPortCommandTaskSummaryBuilderService {
constructor(
private readonly translocoService: TranslocoService
) {
}

public build(
payload: SetLinearSpeedTaskPayload
): Observable<string> {
if (payload.power !== 0 && payload.speed === 0) {
return this.translocoService.selectTranslate('controlScheme.linearBrakeTaskSummary');
}
return this.translocoService.selectTranslate('controlScheme.linearTaskSummary', payload);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Pipe, PipeTransform } from '@angular/core';
import { Observable, filter, switchMap } from 'rxjs';
import { Store } from '@ngrx/store';
import { ATTACHED_IO_PROPS_SELECTORS, AttachedIoPropsModel, PortCommandTask, PortCommandTaskType } from '@app/store';

import { LinearPortCommandTaskSummaryBuilderService } from './linear-port-command-task-summary-builder.service';
import { ServoPortCommandTaskSummaryBuilderService } from './servo-port-command-task-summary-builder.service';
import { SetAnglePortCommandTaskSummaryBuilderService } from './set-angle-port-command-task-summary-builder.service';
import { StepperPortCommandTaskSummaryBuilderService } from './stepper-port-command-task-summary-builder.service';

@Pipe({
standalone: true,
name: 'portCommandTaskSummary',
pure: true
})
export class PortCommandTaskSummaryPipe implements PipeTransform {
constructor(
private readonly linearPortCommandTaskSummaryBuilder: LinearPortCommandTaskSummaryBuilderService,
private readonly setAnglePortCommandTaskSummaryBuilder: SetAnglePortCommandTaskSummaryBuilderService,
private readonly servoPortCommandTaskSummaryBuilder: ServoPortCommandTaskSummaryBuilderService,
private readonly stepperPortCommandTaskSummaryBuilder: StepperPortCommandTaskSummaryBuilderService,
private readonly store: Store
) {
}

public transform(
portCommandTask: PortCommandTask
): Observable<string> {
const payload = portCommandTask.payload;
switch (payload.taskType) {
case PortCommandTaskType.SetSpeed:
return this.linearPortCommandTaskSummaryBuilder.build(payload);
case PortCommandTaskType.SetAngle:
return this.store.select(ATTACHED_IO_PROPS_SELECTORS.selectById(portCommandTask)).pipe(
filter((ioProps): ioProps is AttachedIoPropsModel => !!ioProps),
switchMap((ioProps) => this.setAnglePortCommandTaskSummaryBuilder.build(
ioProps,
payload
))
);
case PortCommandTaskType.Servo:
return this.store.select(ATTACHED_IO_PROPS_SELECTORS.selectById(portCommandTask)).pipe(
filter((ioProps): ioProps is AttachedIoPropsModel => !!ioProps),
switchMap((ioProps) => this.servoPortCommandTaskSummaryBuilder.build(
ioProps,
payload
))
);
case PortCommandTaskType.Stepper:
return this.stepperPortCommandTaskSummaryBuilder.build(payload);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { TranslocoService } from '@ngneat/transloco';
import { AttachedIoPropsModel, ServoTaskPayload } from '@app/store';

@Injectable({ providedIn: 'root' })
export class ServoPortCommandTaskSummaryBuilderService {
constructor(
private readonly translocoService: TranslocoService
) {
}

public build(
attachedIoProps: AttachedIoPropsModel,
payload: ServoTaskPayload
): Observable<string> {
const angle = (attachedIoProps.motorEncoderOffset ?? 0) + payload.angle;
return this.translocoService.selectTranslate('controlScheme.servoTaskSummary', { angle });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { TranslocoService } from '@ngneat/transloco';
import { AttachedIoPropsModel, SetAngleTaskPayload } from '@app/store';

@Injectable({ providedIn: 'root' })
export class SetAnglePortCommandTaskSummaryBuilderService {
constructor(
private readonly translocoService: TranslocoService
) {
}

public build(
attachedIoProps: AttachedIoPropsModel,
payload: SetAngleTaskPayload
): Observable<string> {
const angle = (attachedIoProps.motorEncoderOffset ?? 0) + payload.angle;
return this.translocoService.selectTranslate('controlScheme.setAngleTaskSummary', { angle });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { TranslocoService } from '@ngneat/transloco';
import { StepperTaskPayload } from '@app/store';

@Injectable({ providedIn: 'root' })
export class StepperPortCommandTaskSummaryBuilderService {
constructor(
private readonly translocoService: TranslocoService
) {
}

public build(
payload: StepperTaskPayload
): Observable<string> {
return this.translocoService.selectTranslate('controlScheme.stepperTaskSummary', payload);
}
}
6 changes: 6 additions & 0 deletions src/app/store/selectors/attached-io-props.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ export const ATTACHED_IO_PROPS_SELECTORS = {
ATTACHED_IO_PROPS_FEATURE.selectAttachedIoPropsState,
ATTACHED_IO_PROPS_ENTITY_ADAPTER.getSelectors().selectEntities,
),
selectById: (
q: { hubId: string; portId: number }
) => createSelector(
ATTACHED_IO_PROPS_SELECTORS.selectEntities,
(entities) => entities[hubAttachedIoPropsIdFn(q)]
),
selectMotorEncoderOffset: (
q: { hubId: string; portId: number }
) => createSelector(
Expand Down
13 changes: 9 additions & 4 deletions src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,24 @@
"runningDetailsTotalTasksExecuted": "Total tasks executed:",
"runningDetailsLastTenTasksAverageExecutionTime": "Last 10 tasks average execution time (ms):",
"setAngleInput": "Set angle",
"setAngleOutputConfigurationAngle": "Target angle",
"setAngleOutputConfigurationAngle": "Target angle (°)",
"setAngleTaskSummary": "Set angle: { angle }°",
"linearInputSpeed": "Speed",
"linearInputBrake": "Brake",
"linearOutputConfigurationTreatAsSwitch": "Treat button as a switch",
"linearTaskSummary": "Speed: { speed }",
"linearBrakeTaskSummary": "Brake",
"servoInput": "Servo control",
"servoOutputConfigurationCalibrate": "Calibrate",
"servoOutputConfigurationCenterOffset": "Center offset (deg)",
"servoOutputConfigurationAngleRange": "Servo range (deg)",
"servoOutputConfigurationCenterOffset": "Center offset (°)",
"servoOutputConfigurationAngleRange": "Servo range (°)",
"servoCalibrationIsInProgress": "Servo calibration is in progress",
"servoCalibrationError": "An error occurred during servo calibration",
"servoCalibrationCancel": "Cancel",
"servoTaskSummary": "Servo: { angle }°",
"stepperInput": "Make step",
"stepperOutputConfigurationDegree": "Angle (deg)",
"stepperOutputConfigurationDegree": "Angle (°)",
"stepperTaskSummary": "Step: { degree }°",
"outputSpeedControlTitle": "Speed",
"outputPowerControlTitle": "Power",
"outputInvertControlTitle": "Invert",
Expand Down

0 comments on commit 6a9a0de

Please sign in to comment.