diff --git a/HardwareReport/HardwareReport.js b/HardwareReport/HardwareReport.js index 8054599a..1bf6b4b4 100644 --- a/HardwareReport/HardwareReport.js +++ b/HardwareReport/HardwareReport.js @@ -1671,7 +1671,208 @@ function update_minimal_config() { } } -function load_params(log) { +// Param metadata loaded from params.json +let param_metadata = {} + +// Lookup a value name from param metadata +function get_param_value_name(param_name, param_value) { + function recursive_search(obj, param) { + for (const [key, val] of Object.entries(obj)) { + if (!param.startsWith(key)) continue + if (param === key) return val + let found = recursive_search(val, param) + if (found != null) return found + } + } + const metadata = recursive_search(param_metadata, param_name) + if (metadata != null && "Values" in metadata) { + const str_value = String(param_value) + if (str_value in metadata.Values) { + return metadata.Values[str_value] + } + } + return null +} + +function load_vehicle_config(build_type) { + const section = document.getElementById("VehicleConfig") + + const build_type_names = { + 1: "Rover", + 2: "Copter", + 3: "Plane", + 4: "AntennaTracker", + 7: "Sub", + 12: "Blimp", + } + + let have_content = false + + // Vehicle type from build_type (from log VER/MSG messages) + let vehicle_name + if (build_type != null) { + vehicle_name = build_type_names[build_type] + } + + // Refine Plane sub-types from params + if (vehicle_name === "Plane") { + if (("Q_ENABLE" in params) && (params["Q_ENABLE"] > 0)) { + vehicle_name = "QuadPlane" + if (("Q_TAILSIT_ENABLE" in params) && (params["Q_TAILSIT_ENABLE"] > 0)) { + vehicle_name = "Tailsitter" + } + if (("Q_TILT_ENABLE" in params) && (params["Q_TILT_ENABLE"] > 0)) { + vehicle_name = "Tilt Rotor" + } + } + } + + if (vehicle_name != null) { + section.appendChild(document.createTextNode("Vehicle type: " + vehicle_name)) + section.appendChild(document.createElement("br")) + have_content = true + } + + // Frame class and type for copter/quadplane + let frame_class_param = "FRAME_CLASS" + let frame_type_param = "FRAME_TYPE" + if (("Q_ENABLE" in params) && (params["Q_ENABLE"] > 0)) { + frame_class_param = "Q_FRAME_CLASS" + frame_type_param = "Q_FRAME_TYPE" + } + + if (frame_class_param in params) { + const fc = params[frame_class_param] + const fc_name = get_param_value_name("FRAME_CLASS", fc) || ("Unknown (" + fc + ")") + section.appendChild(document.createTextNode("Frame class: " + fc_name)) + section.appendChild(document.createElement("br")) + have_content = true + + if (frame_type_param in params) { + const ft = params[frame_type_param] + const ft_name = get_param_value_name("FRAME_TYPE", ft) || ("Unknown (" + ft + ")") + section.appendChild(document.createTextNode("Frame type: " + ft_name)) + section.appendChild(document.createElement("br")) + } + } + + if (have_content) { + section.hidden = false + section.previousElementSibling.hidden = false + } +} + +function load_output_config() { + const section = document.getElementById("OutputConfig") + + const max_servo = 32 + let outputs = [] + + for (let i = 1; i <= max_servo; i++) { + const func_name = "SERVO" + i + "_FUNCTION" + if (func_name in params) { + const func_val = params[func_name] + if (func_val != 0) { + const func_label = get_param_value_name("SERVOn_FUNCTION", func_val) || ("Unknown (" + func_val + ")") + outputs.push({ channel: i, func_val: func_val, func_name: func_label }) + } + } + } + + if (outputs.length == 0) { + return + } + + // Gather per-port DroneCAN ESC and Servo bitmasks and index offsets + let dronecan_ports = [] + for (let port = 1; port < 10; port++) { + const protocol = "CAN_P" + port + "_DRIVER" + if (!(protocol in params) || params[protocol] == 0) { + continue + } + const driver_num = params[protocol] + let port_info = { port: port, esc_bm: 0, srv_bm: 0, esc_of: 0, srv_of: 0 } + const esc_bm_name = "CAN_D" + driver_num + "_UC_ESC_BM" + if (esc_bm_name in params) { + port_info.esc_bm = params[esc_bm_name] + } + const srv_bm_name = "CAN_D" + driver_num + "_UC_SRV_BM" + if (srv_bm_name in params) { + port_info.srv_bm = params[srv_bm_name] + } + const esc_of_name = "CAN_D" + driver_num + "_UC_ESC_OF" + if (esc_of_name in params) { + port_info.esc_of = params[esc_of_name] + } + const srv_of_name = "CAN_D" + driver_num + "_UC_SRV_OF" + if (srv_of_name in params) { + port_info.srv_of = params[srv_of_name] + } + if (port_info.esc_bm > 0 || port_info.srv_bm > 0) { + dronecan_ports.push(port_info) + } + } + const have_dronecan = dronecan_ports.length > 0 + + section.hidden = false + section.previousElementSibling.hidden = false + + // Build table + let table = document.createElement("table") + table.style.borderCollapse = "collapse" + section.appendChild(table) + + // Header row + let header = document.createElement("tr") + table.appendChild(header) + + function add_header_cell(text) { + let th = document.createElement("th") + th.style.border = "1px solid #000" + th.style.padding = "6px 12px" + th.style.textAlign = "left" + th.appendChild(document.createTextNode(text)) + header.appendChild(th) + } + add_header_cell("Channel") + add_header_cell("Function") + if (have_dronecan) { + add_header_cell("Output Type") + } + + for (const output of outputs) { + let row = document.createElement("tr") + table.appendChild(row) + + function add_cell(text) { + let td = document.createElement("td") + td.style.border = "1px solid #000" + td.style.padding = "4px 12px" + td.appendChild(document.createTextNode(text)) + row.appendChild(td) + } + + add_cell("SERVO" + output.channel) + add_cell(output.func_name) + if (have_dronecan) { + const bit = 1 << (output.channel - 1) + let output_types = [] + for (const port of dronecan_ports) { + if (port.esc_bm & bit) { + output_types.push("CAN" + port.port + " ESC " + (output.channel - 1 + port.esc_of)) + } + if (port.srv_bm & bit) { + output_types.push("CAN" + port.port + " Servo " + (output.channel - 1 + port.srv_of)) + } + } + add_cell(output_types.join(", ")) + } + } +} + +function load_params(log, build_type) { + load_vehicle_config(build_type) + load_output_config() load_ins(log) load_compass(log) load_baro(log) @@ -2590,10 +2791,10 @@ async function load_log(log_file) { document.getElementById("param_base_changed").checked = true } - load_params(log) - const version = get_version_and_board(log) + load_params(log, version.build_type) + if (version.fw_string != null) { let section = document.getElementById("VER") section.hidden = false @@ -3180,6 +3381,8 @@ function reset() { setup_section(document.getElementById("FC")) setup_section(document.getElementById("WDOG")) setup_section(document.getElementById("InternalError")) + setup_section(document.getElementById("VehicleConfig")) + setup_section(document.getElementById("OutputConfig")) setup_section(document.getElementById("IOMCU")) setup_section(document.getElementById("INS")) setup_section(document.getElementById("COMPASS")) @@ -3660,15 +3863,15 @@ async function initial_load() { } } - return new Promise((resolve) => { - fetch("board_types.txt") - .then((res) => { - return res.text() - }).then((data) => { - load_board_types(data) - resolve() - }); - }) + const board_types_promise = fetch("board_types.txt") + .then((res) => res.text()) + .then((data) => load_board_types(data)) + + const param_metadata_promise = fetch("params.json") + .then((res) => res.json()) + .then((data) => { param_metadata = data }) + + await Promise.all([board_types_promise, param_metadata_promise]) } function save_text(text, file_postfix) { diff --git a/HardwareReport/index.html b/HardwareReport/index.html index 986623a5..16f12dbf 100644 --- a/HardwareReport/index.html +++ b/HardwareReport/index.html @@ -54,6 +54,12 @@

+ +

+ + +

+

diff --git a/HardwareReport/params.json b/HardwareReport/params.json new file mode 100644 index 00000000..c81a9109 --- /dev/null +++ b/HardwareReport/params.json @@ -0,0 +1,225 @@ +{ + "FRAME_": { + "FRAME_CLASS": { + "Description": "Controls major frame class for multicopter component", + "DisplayName": "Frame Class", + "RebootRequired": "True", + "User": "Standard", + "Values": { + "0": "Undefined", + "1": "Quad", + "2": "Hexa", + "3": "Octa", + "4": "OctaQuad", + "5": "Y6", + "6": "Heli", + "7": "Tri", + "8": "SingleCopter", + "9": "CoaxCopter", + "10": "BiCopter", + "11": "Heli_Dual", + "12": "DodecaHexa", + "13": "HeliQuad", + "14": "Deca", + "15": "Scripting Matrix", + "16": "6DoF Scripting", + "17": "Dynamic Scripting Matrix" + } + }, + "FRAME_TYPE": { + "Description": "Controls motor mixing for multicopters. Not used for Tri or Traditional Helicopters.", + "DisplayName": "Frame Type", + "RebootRequired": "True", + "User": "Standard", + "Values": { + "0": "Plus", + "1": "X", + "2": "V", + "3": "H", + "4": "V-Tail", + "5": "A-Tail", + "10": "Y6B", + "11": "Y6F", + "12": "BetaFlightX", + "13": "DJIX", + "14": "ClockwiseX", + "15": "I", + "18": "BetaFlightXReversed", + "19": "Y4" + } + } + }, + "SERVO": { + "SERVOn_FUNCTION": { + "Description": "Function assigned to this servo. Setting this to Disabled(0) will setup this output for control by auto missions or MAVLink servo set commands. any other value will enable the corresponding function", + "DisplayName": "Servo output function", + "RebootRequired": "True", + "User": "Standard", + "Values": { + "-1": "GPIO", + "0": "Disabled", + "1": "RCPassThru", + "2": "Flap", + "3": "FlapAuto", + "4": "Aileron", + "6": "Mount1Yaw", + "7": "Mount1Pitch", + "8": "Mount1Roll", + "9": "Mount1Retract", + "10": "CameraTrigger", + "11": "EggDrop", + "12": "Mount2Yaw", + "13": "Mount2Pitch", + "14": "Mount2Roll", + "15": "Mount2Retract", + "16": "DifferentialSpoilerLeft1", + "17": "DifferentialSpoilerRight1", + "18": "AileronWithInput", + "19": "Elevator", + "20": "ElevatorWithInput", + "21": "Rudder", + "22": "SprayerPump", + "23": "SprayerSpinner", + "24": "FlaperonLeft", + "25": "FlaperonRight", + "26": "GroundSteering", + "27": "Parachute", + "28": "Gripper", + "29": "LandingGear", + "30": "EngineRunEnable", + "31": "HeliRSC", + "32": "HeliTailRSC", + "33": "Motor1", + "34": "Motor2", + "35": "Motor3", + "36": "Motor4", + "37": "Motor5", + "38": "Motor6", + "39": "Motor7", + "40": "Motor8", + "41": "MotorTilt", + "42": "GeneratorControl", + "45": "TiltMotorRear", + "46": "TiltMotorRearLeft", + "47": "TiltMotorRearRight", + "51": "RCIN1", + "52": "RCIN2", + "53": "RCIN3", + "54": "RCIN4", + "55": "RCIN5", + "56": "RCIN6", + "57": "RCIN7", + "58": "RCIN8", + "59": "RCIN9", + "60": "RCIN10", + "61": "RCIN11", + "62": "RCIN12", + "63": "RCIN13", + "64": "RCIN14", + "65": "RCIN15", + "66": "RCIN16", + "67": "Ignition", + "68": "Choke", + "69": "Starter", + "70": "Throttle", + "71": "TrackerYaw", + "72": "TrackerPitch", + "73": "ThrottleLeft", + "74": "ThrottleRight", + "75": "TiltMotorFrontLeft", + "76": "TiltMotorFrontRight", + "77": "ElevonLeft", + "78": "ElevonRight", + "79": "VTailLeft", + "80": "VTailRight", + "81": "BoostThrottle", + "82": "Motor9", + "83": "Motor10", + "84": "Motor11", + "85": "Motor12", + "86": "DifferentialSpoilerLeft2", + "87": "DifferentialSpoilerRight2", + "88": "Winch", + "89": "MainSailSheet", + "90": "CameraISO", + "91": "CameraAperture", + "92": "CameraFocus", + "93": "CameraShutterSpeed", + "94": "Script1", + "95": "Script2", + "96": "Script3", + "97": "Script4", + "98": "Script5", + "99": "Script6", + "100": "Script7", + "101": "Script8", + "102": "Script9", + "103": "Script10", + "104": "Script11", + "105": "Script12", + "106": "Script13", + "107": "Script14", + "108": "Script15", + "109": "Script16", + "110": "Airbrake", + "120": "NeoPixel1", + "121": "NeoPixel2", + "122": "NeoPixel3", + "123": "NeoPixel4", + "124": "RateRoll", + "125": "RatePitch", + "126": "RateThrust", + "127": "RateYaw", + "128": "WingSailElevator", + "129": "ProfiLED1", + "130": "ProfiLED2", + "131": "ProfiLED3", + "132": "ProfiLEDClock", + "133": "Winch Clutch", + "134": "SERVOn_MIN", + "135": "SERVOn_TRIM", + "136": "SERVOn_MAX", + "137": "MastRotation", + "138": "Alarm", + "139": "Alarm Inverted", + "140": "RCIN1Scaled", + "141": "RCIN2Scaled", + "142": "RCIN3Scaled", + "143": "RCIN4Scaled", + "144": "RCIN5Scaled", + "145": "RCIN6Scaled", + "146": "RCIN7Scaled", + "147": "RCIN8Scaled", + "148": "RCIN9Scaled", + "149": "RCIN10Scaled", + "150": "RCIN11Scaled", + "151": "RCIN12Scaled", + "152": "RCIN13Scaled", + "153": "RCIN14Scaled", + "154": "RCIN15Scaled", + "155": "RCIN16Scaled", + "156": "LiftRelease", + "160": "Motor13", + "161": "Motor14", + "162": "Motor15", + "163": "Motor16", + "164": "Motor17", + "165": "Motor18", + "166": "Motor19", + "167": "Motor20", + "168": "Motor21", + "169": "Motor22", + "170": "Motor23", + "171": "Motor24", + "172": "Motor25", + "173": "Motor26", + "174": "Motor27", + "175": "Motor28", + "176": "Motor29", + "177": "Motor30", + "178": "Motor31", + "179": "Motor32" + } + } + } +}