Skip to content

Commit

Permalink
feat: add 'Accelerate' binding type
Browse files Browse the repository at this point in the history
  • Loading branch information
nvsukhanov committed Apr 5, 2024
1 parent 2716d37 commit 8cc4dd4
Show file tree
Hide file tree
Showing 36 changed files with 780 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
@if (form) {
<lib-cs-binding-edit-sections-container>
<lib-cs-binding-edit-section [largeScreenWidthPercentage]="33"
[caption]="'controlScheme.bindingSectionOutputTitle' | transloco"
>
<lib-cs-binding-control-select-hub [control]="form.controls.hubId"
[bindingType]="bindingType"
></lib-cs-binding-control-select-hub>

<lib-cs-binding-control-select-io [portIdControl]="form.controls.portId"
[hubIdControl]="form.controls.hubId"
[bindingType]="bindingType"
></lib-cs-binding-control-select-io>
</lib-cs-binding-edit-section>

<mat-divider [vertical]="true" *libHideOnSmallScreen></mat-divider>

<lib-cs-binding-edit-section [largeScreenWidthPercentage]="33"
[caption]="'controlScheme.bindingSectionInputTitle' | transloco"
>
<lib-cs-binding-control-select-controller [data]="forwardsControlBindingComponentData"></lib-cs-binding-control-select-controller>
<lib-cs-binding-control-select-controller [data]="backwardsControlBindingComponentData"></lib-cs-binding-control-select-controller>
<lib-cs-binding-control-select-controller [data]="slowdownControlBindingComponentData"></lib-cs-binding-control-select-controller>

<mat-error [libValidationMessages]="form.controls.inputs"
[immediatelyShowMessages]="true"
[l10nMap]="validationErrorsMap"
></mat-error>
</lib-cs-binding-edit-section>

<mat-divider [vertical]="true" *libHideOnSmallScreen></mat-divider>

<lib-cs-binding-edit-section [largeScreenWidthPercentage]="33"
[caption]="'controlScheme.bindingSectionConfigTitle' | transloco"
>
<lib-cs-binding-control-speed-input [control]="form.controls.maxSpeed"
[translocoTitle]="'controlScheme.speedBinding.outputSpeed'"
></lib-cs-binding-control-speed-input>

@if (isForwardsControlAssigned) {
<lib-cs-binding-control-speed-input [control]="form.controls.forwardsSpeedIncrement"
[translocoTitle]="'controlScheme.accelerateBinding.forwardsSpeedIncrement'"
></lib-cs-binding-control-speed-input>
}

@if (isBackwardsControlAssigned) {
<lib-cs-binding-control-speed-input [control]="form.controls.backwardsSpeedIncrement"
[translocoTitle]="'controlScheme.accelerateBinding.backwardsSpeedIncrement'"
></lib-cs-binding-control-speed-input>
}

@if (isSlowDownControlAssigned) {
<lib-cs-binding-control-speed-input [control]="form.controls.slowdownSpeedDecrement"
[translocoTitle]="'controlScheme.accelerateBinding.slowdownSpeedDecrement'"
></lib-cs-binding-control-speed-input>
}

<lib-cs-binding-control-power-input [control]="form.controls.power"></lib-cs-binding-control-power-input>

<lib-toggle-control [translocoTitle]="'controlScheme.outputInvertControlTitle'"
[control]="form.controls.invert"
></lib-toggle-control>
</lib-cs-binding-edit-section>
</lib-cs-binding-edit-sections-container>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:host {
display: block;
flex-grow: 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core';
import { merge } from 'rxjs';
import { TranslocoPipe } from '@ngneat/transloco';
import { MatDividerModule } from '@angular/material/divider';
import { MatError } from '@angular/material/form-field';
import { ControlSchemeBindingType, ValidationErrorsL10nMap, ValidationMessagesDirective } from '@app/shared-misc';
import { HideOnSmallScreenDirective, ToggleControlComponent } from '@app/shared-components';
import { AccelerateBindingInputAction, SpeedBindingInputAction } from '@app/store';
import { BindingControlSelectHubComponent, BindingControlSelectIoComponent } from '@app/shared-control-schemes';

import { IBindingsDetailsEditComponent } from '../i-bindings-details-edit-component';
import {
BindingControlPowerInputComponent,
BindingControlSelectControllerComponent,
BindingControlSelectControllerComponentData,
BindingControlSpeedInputComponent,
BindingEditSectionComponent,
BindingEditSectionsContainerComponent
} from '../common';
import { AccelerateBindingForm } from './accelerate-binding-form';
import { NO_INPUTS_ACCELERATE_ERROR } from './accelerate-binding-form-builder.service';
import { AccelerateBindingL10nService } from './accelerate-binding-l10n.service';

@Component({
standalone: true,
selector: 'lib-cs-speed-binding-edit',
templateUrl: './accelerate-binding-edit.component.html',
styleUrls: [ './accelerate-binding-edit.component.scss' ],
imports: [
BindingEditSectionComponent,
TranslocoPipe,
BindingControlSelectHubComponent,
BindingControlSelectIoComponent,
MatDividerModule,
BindingControlSelectControllerComponent,
HideOnSmallScreenDirective,
ToggleControlComponent,
BindingEditSectionsContainerComponent,
BindingControlPowerInputComponent,
BindingControlSpeedInputComponent,
MatError,
ValidationMessagesDirective
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AccelerateBindingEditComponent implements IBindingsDetailsEditComponent<AccelerateBindingForm> {
public readonly validationErrorsMap: ValidationErrorsL10nMap = {
[NO_INPUTS_ACCELERATE_ERROR]: 'controlScheme.accelerateBinding.missingInputs'
};

public readonly bindingType = ControlSchemeBindingType.Speed;

public form?: AccelerateBindingForm;

private _forwardsControlBindingComponentData: BindingControlSelectControllerComponentData<ControlSchemeBindingType.Speed> | null = null;

private _backwardsControlBindingComponentData: BindingControlSelectControllerComponentData<ControlSchemeBindingType.Speed> | null = null;

private _slowdownControlBindingComponentData: BindingControlSelectControllerComponentData<ControlSchemeBindingType.Speed> | null = null;

constructor(
private readonly cd: ChangeDetectorRef,
private readonly l10nService: AccelerateBindingL10nService
) {
}

public get forwardsControlBindingComponentData(): BindingControlSelectControllerComponentData<ControlSchemeBindingType.Speed> | null {
return this._forwardsControlBindingComponentData;
}

public get backwardsControlBindingComponentData(): BindingControlSelectControllerComponentData<ControlSchemeBindingType.Speed> | null {
return this._backwardsControlBindingComponentData;
}

public get slowdownControlBindingComponentData(): BindingControlSelectControllerComponentData<ControlSchemeBindingType.Speed> | null {
return this._slowdownControlBindingComponentData;
}

public get isForwardsControlAssigned(): boolean {
return !!this.form?.controls.inputs.controls[SpeedBindingInputAction.Forwards].controls.controllerId.value;
}

public get isBackwardsControlAssigned(): boolean {
return !!this.form?.controls.inputs.controls[SpeedBindingInputAction.Backwards].controls.controllerId.value;
}

public get isSlowDownControlAssigned(): boolean {
return !!this.form?.controls.inputs.controls[SpeedBindingInputAction.Brake].controls.controllerId.value;
}

public setForm(
outputBinding: AccelerateBindingForm
): void {
const forwardsControls = outputBinding.controls.inputs.controls[SpeedBindingInputAction.Forwards].controls;
const backwardsControls = outputBinding.controls.inputs.controls[SpeedBindingInputAction.Backwards].controls;
const slowdownControls = outputBinding.controls.inputs.controls[SpeedBindingInputAction.Brake].controls;
if (outputBinding !== this.form) {
this.form = outputBinding;

this._forwardsControlBindingComponentData = {
bindingType: ControlSchemeBindingType.Speed,
inputFormGroup: outputBinding.controls.inputs.controls[AccelerateBindingInputAction.Forwards],
inputAction: SpeedBindingInputAction.Forwards,
inputName$: this.l10nService.getBindingInputName(AccelerateBindingInputAction.Forwards),
supportedInputPipes: [ ]
};
this._backwardsControlBindingComponentData = {
bindingType: ControlSchemeBindingType.Speed,
inputFormGroup: outputBinding.controls.inputs.controls[AccelerateBindingInputAction.Backwards],
inputAction: SpeedBindingInputAction.Backwards,
inputName$: this.l10nService.getBindingInputName(AccelerateBindingInputAction.Backwards),
supportedInputPipes: [ ]
};
this._slowdownControlBindingComponentData = {
bindingType: ControlSchemeBindingType.Speed,
inputFormGroup: outputBinding.controls.inputs.controls[AccelerateBindingInputAction.Slowdown],
inputAction: SpeedBindingInputAction.Brake,
inputName$: this.l10nService.getBindingInputName(AccelerateBindingInputAction.Slowdown),
supportedInputPipes: [ ]
};

merge(
forwardsControls.controllerId.valueChanges,
forwardsControls.inputId.valueChanges,
forwardsControls.inputType.valueChanges,
backwardsControls.controllerId.valueChanges,
backwardsControls.inputId.valueChanges,
backwardsControls.inputType.valueChanges,
slowdownControls.controllerId.valueChanges,
slowdownControls.inputId.valueChanges,
slowdownControls.inputType.valueChanges
).subscribe(() => {
this.cd.markForCheck();
});
this.cd.markForCheck();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Inject, Injectable } from '@angular/core';
import { FormBuilder, ValidationErrors, ValidatorFn } from '@angular/forms';
import { ControlSchemeAccelerateBinding, SpeedBindingInputAction } from '@app/store';
import { ControlSchemeFormBuilderService } from '@app/shared-control-schemes';
import { APP_CONFIG, IAppConfig } from '@app/shared-misc';

import { CommonBindingsFormControlsBuilderService } from '../common';
import { IBindingFormBuilder } from '../i-binding-form-builder';
import { AccelerateBindingForm } from './accelerate-binding-form';

export const NO_INPUTS_ACCELERATE_ERROR = 'NO_SET_ACCELERATE_INPUTS_ERROR';

@Injectable()
export class AccelerateBindingFormBuilderService implements IBindingFormBuilder<AccelerateBindingForm> {
constructor(
private readonly formBuilder: FormBuilder,
private commonFormControlBuilder: CommonBindingsFormControlsBuilderService,
private readonly controlSchemeFormBuilder: ControlSchemeFormBuilderService,
@Inject(APP_CONFIG) private readonly appConfig: IAppConfig
) {
}

public build(): AccelerateBindingForm {
return this.formBuilder.group({
inputs: this.formBuilder.group({
[SpeedBindingInputAction.Forwards]: this.commonFormControlBuilder.optionalInputFormGroup(),
[SpeedBindingInputAction.Backwards]: this.commonFormControlBuilder.optionalInputFormGroup(),
[SpeedBindingInputAction.Brake]: this.commonFormControlBuilder.optionalInputFormGroup(),
}, {
validators: this.createInputsValidators()
}),
forwardsSpeedIncrement: this.commonFormControlBuilder.speedControl(this.appConfig.acceleration.defaultAccelerationStep),
backwardsSpeedIncrement: this.commonFormControlBuilder.speedControl(this.appConfig.acceleration.defaultAccelerationStep),
slowdownSpeedDecrement: this.commonFormControlBuilder.speedControl(this.appConfig.acceleration.defaultAccelerationStep),
hubId: this.controlSchemeFormBuilder.hubIdControl(),
portId: this.controlSchemeFormBuilder.portIdControl(),
maxSpeed: this.commonFormControlBuilder.speedControl(),
invert: this.commonFormControlBuilder.toggleControl(),
power: this.commonFormControlBuilder.powerControl(),
});
}

public patchForm(
form: AccelerateBindingForm,
patch: ControlSchemeAccelerateBinding
): void {
form.patchValue(patch);
this.commonFormControlBuilder.patchInputPipes(
form.controls.inputs.controls[SpeedBindingInputAction.Forwards].controls.inputPipes,
patch.inputs?.[SpeedBindingInputAction.Forwards]?.inputPipes ?? []
);
this.commonFormControlBuilder.patchInputPipes(
form.controls.inputs.controls[SpeedBindingInputAction.Backwards].controls.inputPipes,
patch.inputs?.[SpeedBindingInputAction.Backwards]?.inputPipes ?? []
);
this.commonFormControlBuilder.patchInputPipes(
form.controls.inputs.controls[SpeedBindingInputAction.Brake].controls.inputPipes,
patch.inputs?.[SpeedBindingInputAction.Brake]?.inputPipes ?? []
);
}

private createInputsValidators(): ValidatorFn {
const VALIDATOR = (inputsGroup: AccelerateBindingForm['controls']['inputs']): ValidationErrors | null => {
const forwards = inputsGroup.value[SpeedBindingInputAction.Forwards];
const backwards = inputsGroup.value[SpeedBindingInputAction.Backwards];
const brake = inputsGroup.value[SpeedBindingInputAction.Brake];

if (forwards?.controllerId === null && backwards?.controllerId === null && brake?.controllerId === null) {
return { [NO_INPUTS_ACCELERATE_ERROR]: true };
}
return null;
};
// ValidatorFn expects an AbstractControl as the first argument, which is not typed, but it is a subclass of FormGroup
// So we can safely cast it to ValidatorFn and keep the type safety
return VALIDATOR as ValidatorFn;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Injectable } from '@angular/core';
import { ControlSchemeBindingType } from '@app/shared-misc';
import { ControlSchemeAccelerateBinding, ControlSchemeBinding, SpeedBindingInputAction } from '@app/store';

import { CommonFormMapperService, InputFormGroup } from '../common';
import { IBindingFormMapper } from '../i-binding-form-mapper';
import { AccelerateBindingForm } from './accelerate-binding-form';

@Injectable()
export class AccelerateBindingFormMapperService implements IBindingFormMapper<AccelerateBindingForm, ControlSchemeAccelerateBinding> {
constructor(
private readonly commonFormMapperService: CommonFormMapperService
) {
}

public mapToModel(
id: ControlSchemeBinding['id'],
form: AccelerateBindingForm
): ControlSchemeAccelerateBinding {
const hubId = form.controls.hubId.value;
const portId = form.controls.portId.value;
if (hubId === null || portId === null) {
throw new Error('Hub ID and port ID must be set');
}
const result: ControlSchemeAccelerateBinding = {
id,
bindingType: ControlSchemeBindingType.Accelerate,
inputs: {},
forwardsSpeedIncrement: form.controls.forwardsSpeedIncrement.getRawValue(),
backwardsSpeedIncrement: form.controls.backwardsSpeedIncrement.getRawValue(),
slowdownSpeedDecrement: form.controls.slowdownSpeedDecrement.getRawValue(),
hubId,
portId,
maxSpeed: form.controls.maxSpeed.getRawValue(),
invert: form.controls.invert.getRawValue(),
power: form.controls.power.getRawValue(),
};
if (form.controls.inputs.controls[SpeedBindingInputAction.Forwards].controls.controllerId.value !== null) {
result.inputs[SpeedBindingInputAction.Forwards]
= this.commonFormMapperService.mapInputFormToSchemeInput(form.controls.inputs.controls[SpeedBindingInputAction.Forwards] as InputFormGroup);
}
if (form.controls.inputs.controls[SpeedBindingInputAction.Backwards].controls.controllerId.value !== null) {
result.inputs[SpeedBindingInputAction.Backwards]
= this.commonFormMapperService.mapInputFormToSchemeInput(form.controls.inputs.controls[SpeedBindingInputAction.Backwards] as InputFormGroup);
}
if (form.controls.inputs.controls[SpeedBindingInputAction.Brake].controls.controllerId.value !== null) {
result.inputs[SpeedBindingInputAction.Brake]
= this.commonFormMapperService.mapInputFormToSchemeInput(form.controls.inputs.controls[SpeedBindingInputAction.Brake] as InputFormGroup);
}
return result;
}
}
20 changes: 20 additions & 0 deletions modules/bindings/src/lib/accelerate/accelerate-binding-form.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { FormControl, FormGroup } from '@angular/forms';
import { SpeedBindingInputAction } from '@app/store';

import { OptionalInputFormGroup } from '../common';

export type AccelerateBindingForm = FormGroup<{
inputs: FormGroup<{
[SpeedBindingInputAction.Forwards]: OptionalInputFormGroup;
[SpeedBindingInputAction.Backwards]: OptionalInputFormGroup;
[SpeedBindingInputAction.Brake]: OptionalInputFormGroup;
}>;
forwardsSpeedIncrement: FormControl<number>;
backwardsSpeedIncrement: FormControl<number>;
slowdownSpeedDecrement: FormControl<number>;
hubId: FormControl<string | null>;
portId: FormControl<number | null>;
maxSpeed: FormControl<number>;
invert: FormControl<boolean>;
power: FormControl<number>;
}>;
Loading

0 comments on commit 8cc4dd4

Please sign in to comment.