From e9180c71c7e8cab9c308a3cd1ce1f10b1899b0fb Mon Sep 17 00:00:00 2001 From: Tomas Chmelevskij Date: Wed, 27 Dec 2023 08:39:24 +0100 Subject: [PATCH] feat: add list of commands with no arguments --- src/js/msp-cli/msp-cli.js | 179 ++++++++++++++++++++ src/js/msp-cli/msp-cli.test.js | 290 ++++++++++++++++++++++++++------- 2 files changed, 410 insertions(+), 59 deletions(-) create mode 100644 src/js/msp-cli/msp-cli.js diff --git a/src/js/msp-cli/msp-cli.js b/src/js/msp-cli/msp-cli.js new file mode 100644 index 0000000000..bc8884f148 --- /dev/null +++ b/src/js/msp-cli/msp-cli.js @@ -0,0 +1,179 @@ +/** ::= "board_name" + * | "board_name" + * | "dshot_telemetry_info" + * | "exit" + * | "mcu_id" + * | "rc_smoothing_info" + * | "save" + * | "sd_info" + * | "status" + * | "tasks" + * | "version" + * | "vtx_info" + * + * ::= + * + * ::= + * | + * + * ::= "a" | "b" | "c" | ... | "z" + * | "A" | "B" | "C" | ... | "Z" + * | "0" | "1" | ... | "9" + * | "_" + */ + +import MSPCodes from "../msp/MSPCodes"; + +function validateCraftName(name) { + // only extend ascii characters are allowed + // up to 16 characters in length + return /^[\x00-\xFF]{1,16}$/.test(name); +} + +const encoder = new TextEncoder(); + +// TODO: some commands return single item, some array, need +// to think which api is better in long run +const MSP_COMMANDS = { + // TODO: should we support old board name based on MSP1? + board_name(args) { + if (args.length === 0) { + return { + code: MSPCodes.MSP2_GET_TEXT, + data: [MSPCodes.CRAFT_NAME], + }; + } + // TODO: what happens if we pass in more arguments + // TODO: what happens if we pass in space into name argument? + if (args.length === 1) { + const isValidName = validateCraftName(args[0]); + if (isValidName) { + return { + code: MSPCodes.MSP2_SET_TEXT, + // TODO: shoule we validate name + data: [ + MSPCodes.CRAFT_NAME, + ...encoder.encode(args[0]), + ], + }; + } else { + // TODO: should we throw an error here? + throw new Error("Invalid craft name"); + } + } + }, + // TODO: what should we be doing with multiple msp commands? + version() { + return [ + { + code: MSPCodes.MSP_BOARD_INFO, + }, + { + code: MSPCodes.MSP_BUILD_INFO, + }, + { + code: MSPCodes.MSP_API_VERSION, + }, + ]; + }, + status() { + return [ + { + code: MSPCodes.MSP_BOARD_INFO, + }, + { + code: MSPCodes.MSP_STATUS, // MSP_STATUS_EX seems to return the same thing + }, + { + code: MSPCodes.MSP2_SENSOR_CONFIG_ACTIVE, + }, + { + code: MSPCodes.MSP2_GET_TEXT, + data: [MSPCodes.BUILD_KEY], + }, + { + code: MSPCodes.MSP2_GET_TEXT, + // NOTE: seems to be missing from our constants + data: [MSPCodes.MSP2TEXT_RELEASENAME], + }, + { + code: MSPCodes.MSP_RTC, + }, + { + code: MSPCodes.MSP_BATTERY_STATE, // MSP_ANALOG seems to have similar values think when we could use it? + }, + { + code: MSPCodes.MSP_SDCARD_SUMMARY, // need to verify if this is enough + }, + { + code: MSPCodes.MSP_GPS_CONFIG, + }, + { + code: MSPCodes.MSP_RAW_GPS, + }, + { + code: MSPCodes.MSP_COMP_GPS, + }, + { + code: MSPCodes.MSP_GPSSVINFO, // this seems to only exist in firmware but not configurator atm + }, + ]; + }, + tasks() { + // NOTE: literally nothing in MSP is obviously matching this CLI command + throw new Error("Not implemented"); + }, + dshot_telemetry_info() { + return { + code: MSPCodes.MSP_MOTOR_TELEMETRY, + }; + }, + exit() { + // NOTE does it even make sense to have `exit` if it's msp driven? + throw new Error("Not implemented"); + }, + mcu_id() { + return { + code: MSPCodes.MSP_UID, + }; + }, + rc_smoothing_info() { + return { + code: MSPCodes.MSP_RX_CONFIG, + }; + }, + save() { + return [ + { + code: MSPCodes.MSP_SET_BOARD_INFO, // not sure, because that one gets input and saves + }, + { + code: MSPCodes.MSP_SET_SIGNATURE, // not sure, because that one gets input and saves + }, + ]; + }, + sd_info() { + return { + code: MSPCodes.MSP_SDCARD_SUMMARY, + }; + }, + vtx_info() { + return { + code: MSPCodes.MSP2_GET_VTX_DEVICE_STATUS, // or MSP_VTX_CONFIG? + }; + }, +}; + +// TODO: think if it's worth splitting this +// up into tokenization and parsing +export function parse(input) { + const parts = input.trim().split(/\s+/); + const commandName = parts[0].toLowerCase(); + const args = parts.slice(1); + + if (commandName in MSP_COMMANDS) { + return MSP_COMMANDS[commandName](args); + } else { + throw new Error("Unsupported command"); + } +} diff --git a/src/js/msp-cli/msp-cli.test.js b/src/js/msp-cli/msp-cli.test.js index 80883ee23e..afb3d5b428 100644 --- a/src/js/msp-cli/msp-cli.test.js +++ b/src/js/msp-cli/msp-cli.test.js @@ -1,4 +1,6 @@ import { describe, expect, it } from "vitest"; +import { parse } from "./msp-cli"; +import MSPCodes from "../msp/MSPCodes"; /** * full command list of cli commands in firmware: @@ -8,63 +10,233 @@ import { describe, expect, it } from "vitest"; */ describe("MSP CLI", () => { - describe.todo('adjrange'); - describe.todo('aux'); - describe.todo('batch'); - describe.todo('beacon'); - describe.todo('beeper'); - describe.todo('bind_rx'); - describe.todo('bl'); - describe.todo('board_name'); - describe.todo('color'); - describe.todo('defaults'); - describe.todo('diff'); - describe.todo('dma'); - describe.todo('dshot_telemetry_info'); - describe.todo('dshotprog'); - describe.todo('dump'); - describe.todo('escprog'); - describe.todo('exit'); - describe.todo('feature'); - describe.todo('flash_erase'); - describe.todo('flash_info'); - describe.todo('flash_read'); - describe.todo('flash_scan'); - describe.todo('flash_write'); - describe.todo('get'); - describe.todo('gpspassthrough'); - describe.todo('gyroregisters'); - describe.todo('help'); - describe.todo('led'); - describe.todo('manufacturer_id'); - describe.todo('map'); - describe.todo('mcu_id'); - describe.todo('mixer'); - describe.todo('mmix'); - describe.todo('mode_color'); - describe.todo('motor'); - describe.todo('msc'); - describe.todo('play_sound'); - describe.todo('profile'); - describe.todo('rateprofile'); - describe.todo('rc_smoothing_info'); - describe.todo('resource'); - describe.todo('rxfail'); - describe.todo('rxrange'); - describe.todo('save'); - describe.todo('sd_info'); - describe.todo('serial'); - describe.todo('serialpassthrough'); - describe.todo('servo'); - describe.todo('set'); - describe.todo('signature'); - describe.todo('simplified_tuning'); - describe.todo('smix'); - describe.todo('status'); - describe.todo('tasks'); - describe.todo('timer'); - describe.todo('version'); - describe.todo('vtx'); - describe.todo('vtx_info'); - describe.todo('vtxtable'); + describe.todo("aux"); + describe.todo("batch"); + describe.todo("beacon"); + describe.todo("beeper"); + describe.todo("bind_rx"); + describe.todo("bl"); + + describe("board_name", () => { + it("should return the board name msp code", () => { + const input = "board_name"; + const expected = { + code: MSPCodes.MSP2_GET_TEXT, + data: [MSPCodes.CRAFT_NAME], + }; + expect(parse(input)).toEqual(expected); + }); + + it("should return set text msp with craft name data", () => { + const input = "board_name test"; + const expected = { + code: MSPCodes.MSP2_SET_TEXT, + data: [ + MSPCodes.CRAFT_NAME, + ...new TextEncoder().encode("test"), + ], + }; + expect(parse(input)).toEqual(expected); + }); + + it("should throw an error for invalid craft name", () => { + const input = "board_name invalid🚁name"; + expect(() => parse(input)).toThrow("Invalid craft name"); + }); + }); + + describe.todo("color"); + describe.todo("defaults"); + describe.todo("diff"); + describe.todo("dma"); + + describe("dshot_telemetry_info", () => { + it("should return the dshot telemetry info MSP code", () => { + const input = "dshot_telemetry_info"; + const expected = { + code: MSPCodes.MSP_MOTOR_TELEMETRY, + }; + expect(parse(input)).toEqual(expected); + }); + }); + + describe.todo("dshotprog"); + describe.todo("dump"); + describe.todo("escprog"); + + describe("exit", () => { + it("should throw an error for not implemented command", () => { + const input = "exit"; + expect(() => parse(input)).toThrow("Not implemented"); + }); + }); + + describe.todo("feature"); + describe.todo("flash_erase"); + describe.todo("flash_info"); + describe.todo("flash_read"); + describe.todo("flash_scan"); + describe.todo("flash_write"); + describe.todo("get"); + describe.todo("gpspassthrough"); + describe.todo("gyroregisters"); + describe.todo("help"); + describe.todo("led"); + describe.todo("manufacturer_id"); + describe.todo("map"); + + describe("mcu_id", () => { + it("should return the mcu id MSP code", () => { + const input = "mcu_id"; + const expected = { + code: MSPCodes.MSP_UID, + }; + expect(parse(input)).toEqual(expected); + }); + }); + + describe.todo("mcu_id"); + describe.todo("mixer"); + describe.todo("mmix"); + describe.todo("mode_color"); + describe.todo("motor"); + describe.todo("msc"); + describe.todo("play_sound"); + describe.todo("profile"); + describe.todo("rateprofile"); + + describe("rc_smoothing_info", () => { + it("should return the rc smoothing info MSP code", () => { + const input = "rc_smoothing_info"; + const expected = { + code: MSPCodes.MSP_RX_CONFIG, + }; + expect(parse(input)).toEqual(expected); + }); + }); + + describe.todo("resource"); + describe.todo("rxfail"); + describe.todo("rxrange"); + + describe("save", () => { + it("should return an array of save MSP codes", () => { + const input = "save"; + const expected = [ + { + code: MSPCodes.MSP_SET_BOARD_INFO, + }, + { + code: MSPCodes.MSP_SET_SIGNATURE, + }, + ]; + expect(parse(input)).toEqual(expected); + }); + }); + + describe("sd_info", () => { + it("should return the sd card info MSP code", () => { + const input = "sd_info"; + const expected = { + code: MSPCodes.MSP_SDCARD_SUMMARY, + }; + expect(parse(input)).toEqual(expected); + }); + }); + + describe.todo("serial"); + describe.todo("serialpassthrough"); + describe.todo("servo"); + describe.todo("set"); + describe.todo("signature"); + describe.todo("simplified_tuning"); + describe.todo("smix"); + + describe("status", () => { + it("should return an array of status MSP codes", () => { + const input = "status"; + const expected = [ + { + code: MSPCodes.MSP_BOARD_INFO, + }, + { + code: MSPCodes.MSP_STATUS, + }, + { + code: MSPCodes.MSP2_SENSOR_CONFIG_ACTIVE, + }, + { + code: MSPCodes.MSP2_GET_TEXT, + data: [MSPCodes.BUILD_KEY], + }, + { + code: MSPCodes.MSP2_GET_TEXT, + data: [MSPCodes.MSP2TEXT_RELEASENAME], + }, + { + code: MSPCodes.MSP_RTC, + }, + { + code: MSPCodes.MSP_BATTERY_STATE, + }, + { + code: MSPCodes.MSP_SDCARD_SUMMARY, + }, + { + code: MSPCodes.MSP_GPS_CONFIG, + }, + { + code: MSPCodes.MSP_RAW_GPS, + }, + { + code: MSPCodes.MSP_COMP_GPS, + }, + { + code: MSPCodes.MSP_GPSSVINFO, + }, + ]; + expect(parse(input)).toEqual(expected); + }); + }); + + describe("tasks", () => { + it("should throw an error for not implemented command", () => { + const input = "tasks"; + expect(() => parse(input)).toThrow("Not implemented"); + }); + }); + + describe.todo("timer"); + + describe("version", () => { + it("should return an array of version MSP codes", () => { + const input = "version"; + const expected = [ + { + code: MSPCodes.MSP_BOARD_INFO, + }, + { + code: MSPCodes.MSP_BUILD_INFO, + }, + { + code: MSPCodes.MSP_API_VERSION, + }, + ]; + expect(parse(input)).toEqual(expected); + }); + }); + + describe.todo("vtx"); + + describe("vtx_info", () => { + it("should return the vtx info MSP code", () => { + const input = "vtx_info"; + const expected = { + code: MSPCodes.MSP2_GET_VTX_DEVICE_STATUS, + }; + expect(parse(input)).toEqual(expected); + }); + }); + + describe.todo("vtxtable"); });