Files
NODEDC_MISSION_CORE/plugins/vesc/frontend/src/model.ts
T

70 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type {Sensor} from '../../../../packages/sensor-ui/src/contracts';
export interface VescIdentity {uuid:string;hardware:string;version:string;test_firmware:number|null;hardware_type:number|null}
export interface VescTelemetry {observed_at:string;values:Record<string,number|boolean>}
export interface DriveProfile {layout:null|'1x1'|'2x2';revision:number;bindings:Record<string,{device_id:string;uuid:string}>}
export function drivePositions(layout:DriveProfile['layout']):Record<string,string> {
return layout==='2x2'
? {'left.1':'Левый передний','left.2':'Левый задний','right.1':'Правый передний','right.2':'Правый задний'}
: {'left.1':'Левый','right.1':'Правый'};
}
export interface VescStatus {
board_settings_supported?:boolean;
group_test_supported?:boolean;
link_check_supported?:boolean;
foc_calibration?:{min_power_loss_w:number;max_power_loss_w:number;interruptible:boolean};
speed_limits?:{min_erpm:number;max_erpm:number;duration_basis:string;reverse_supported?:boolean;standstill_confirmation_required?:boolean};
hall_measurement?:{current_a:number;interruptible:boolean;standstill_confirmation_required?:boolean};
drive_profile?:DriveProfile;
test_limits?:{min_current_a:number;max_current_a:number;min_duration_s:number;max_duration_s:number;current_ramp_a_per_s?:number;continuous_current?:boolean;max_erpm?:number;max_duty?:number;stall_current_a?:number;stall_timeout_s?:number};
identity:VescIdentity|null; readable:boolean; message:string|null; telemetry:VescTelemetry|null;
backup:{observed_at:string;operation_id:string;configs:Record<string,{bytes:number;sha256:string}>}|null;
}
export function driveTestIds(profile:DriveProfile|undefined,selected:string):string[] {
if(!profile?.layout)return [];
const slots=Object.keys(drivePositions(profile.layout));
if(Object.keys(profile.bindings).length!==slots.length||slots.some(slot=>!profile.bindings[slot]))return [];
const ids=slots.map(slot=>profile.bindings[slot].device_id);
return new Set(ids).size===ids.length&&ids.includes(selected)?ids:[];
}
export function speedInput(speed:string,limits:VescStatus['speed_limits']) {
const erpm=Number(speed);
const error=!limits?'Для удержания скорости требуется обновление профиля VESC на борту.':
speed.trim()===''||!Number.isFinite(erpm)?'Введите скорость.':
erpm<limits.min_erpm||erpm>limits.max_erpm?`Скорость должна быть от ${limits.min_erpm} до ${limits.max_erpm} ERPM.`:null;
return {erpm,error};
}
export function rotationResult(value:{outcome?:string;rotation_s?:number;release_confirmed?:boolean;limits_restored?:boolean}) {
const time=(value.rotation_s??0).toLocaleString('ru-RU',{maximumFractionDigits:1});
const outcome=value.outcome==='duration'?'Заданное время вращения набрано.':value.outcome==='stopped'?'Проверка остановлена.':String(value.outcome);
return `${outcome} Вращение на заданной скорости по данным VESC: ${time} с. ${value.release_confirmed?'Снятие тока подтверждено.':'Снятие тока не подтверждено.'}${value.limits_restored?' Исходные токовые пределы восстановлены.':''}`;
}
export function vescStatus(device:Sensor):VescStatus {
return {identity:null,readable:false,message:null,telemetry:null,backup:null,...device.vesc_status} as VescStatus;
}
export function vescLabel(device:Sensor,fresh:boolean):{label:string;tone:'neutral'|'success'|'warning'|'danger'} {
const status=vescStatus(device);
if(!fresh||!device.online)return {label:'Нет связи',tone:'neutral'};
if(!device.prepared)return {label:'Требуется подготовка',tone:'neutral'};
if(!status.identity)return {label:'Не определён',tone:'warning'};
if(!status.readable)return {label:'Прошивка не поддерживается',tone:'warning'};
return {label:'Готов к чтению',tone:'success'};
}
export function testInput(current:string,duration:string,limits:VescStatus['test_limits']) {
const currentA=Number(current),durationS=Number(duration);
const number=(value:number)=>value.toLocaleString('ru-RU');
const currentError=!limits?null:current.trim()===''||!Number.isFinite(currentA)
? 'Введите ток мотора.'
: currentA<limits.min_current_a||currentA>limits.max_current_a
? `Ток должен быть от ${number(limits.min_current_a)} до ${number(limits.max_current_a)} А.`:null;
const durationError=!limits?null:duration.trim()===''||!Number.isFinite(durationS)
? 'Введите длительность проверки.'
: durationS<limits.min_duration_s||durationS>limits.max_duration_s
? `Длительность должна быть от ${number(limits.min_duration_s)} до ${number(limits.max_duration_s)} с.`:null;
return {currentA,durationS,currentError,durationError,valid:!!limits&&!currentError&&!durationError};
}