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} export interface DriveProfile {layout:null|'1x1'|'2x2';revision:number;bindings:Record} export function drivePositions(layout:DriveProfile['layout']):Record { 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}|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)?'Введите скорость.': erpmlimits.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) ? 'Введите ток мотора.' : currentAlimits.max_current_a ? `Ток должен быть от ${number(limits.min_current_a)} до ${number(limits.max_current_a)} А.`:null; const durationError=!limits?null:duration.trim()===''||!Number.isFinite(durationS) ? 'Введите длительность проверки.' : durationSlimits.max_duration_s ? `Длительность должна быть от ${number(limits.min_duration_s)} до ${number(limits.max_duration_s)} с.`:null; return {currentA,durationS,currentError,durationError,valid:!!limits&&!currentError&&!durationError}; }