Sync inav to Gitea
Make sure docs are updated / settings_md (push) Canceled after 0s
Build firmware / test (push) Canceled after 0s
Build firmware / build-SITL-Windows (push) Canceled after 0s
Build firmware / build-SITL-Mac (push) Canceled after 0s
Build firmware / build-SITL-Linux (push) Canceled after 0s
Build firmware / build-SITL-Linux-arm64 (push) Canceled after 0s
Build firmware / upload-artifacts (push) Canceled after 0s
Build firmware / build-single-target (push) Canceled after 0s
Build firmware / build (9) (push) Canceled after 0s
Build firmware / build (8) (push) Canceled after 0s
Build firmware / build (7) (push) Canceled after 0s
Build firmware / build (6) (push) Canceled after 0s
Build firmware / build (5) (push) Canceled after 0s
Build firmware / build (4) (push) Canceled after 0s
Build firmware / build (3) (push) Canceled after 0s
Build firmware / build (2) (push) Canceled after 0s
Build firmware / build (14) (push) Canceled after 0s
Build firmware / build (13) (push) Canceled after 0s
Build firmware / build (12) (push) Canceled after 0s
Build firmware / build (11) (push) Canceled after 0s
Build firmware / build (10) (push) Canceled after 0s
Build firmware / build (1) (push) Canceled after 0s
Build firmware / build (0) (push) Canceled after 0s
Build firmware / detect (push) Canceled after 0s
Build pre-release / build (push) Canceled after 0s
Build pre-release / Release (push) Canceled after 0s
Make sure docs are updated / settings_md (push) Canceled after 0s
Build firmware / test (push) Canceled after 0s
Build firmware / build-SITL-Windows (push) Canceled after 0s
Build firmware / build-SITL-Mac (push) Canceled after 0s
Build firmware / build-SITL-Linux (push) Canceled after 0s
Build firmware / build-SITL-Linux-arm64 (push) Canceled after 0s
Build firmware / upload-artifacts (push) Canceled after 0s
Build firmware / build-single-target (push) Canceled after 0s
Build firmware / build (9) (push) Canceled after 0s
Build firmware / build (8) (push) Canceled after 0s
Build firmware / build (7) (push) Canceled after 0s
Build firmware / build (6) (push) Canceled after 0s
Build firmware / build (5) (push) Canceled after 0s
Build firmware / build (4) (push) Canceled after 0s
Build firmware / build (3) (push) Canceled after 0s
Build firmware / build (2) (push) Canceled after 0s
Build firmware / build (14) (push) Canceled after 0s
Build firmware / build (13) (push) Canceled after 0s
Build firmware / build (12) (push) Canceled after 0s
Build firmware / build (11) (push) Canceled after 0s
Build firmware / build (10) (push) Canceled after 0s
Build firmware / build (1) (push) Canceled after 0s
Build firmware / build (0) (push) Canceled after 0s
Build firmware / detect (push) Canceled after 0s
Build pre-release / build (push) Canceled after 0s
Build pre-release / Release (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
# INAV Constants Generator
|
||||
|
||||
## Purpose
|
||||
|
||||
Generates `inav_constants.js` from the INAV firmware header file (`logic_condition.h`) to ensure the transpiler/decompiler constants exactly match the actual firmware.
|
||||
|
||||
## Single Source of Truth Architecture
|
||||
|
||||
**Firmware Header** (`logic_condition.h`)
|
||||
↓ (parse)
|
||||
**Constants File** (`inav_constants.js`) - AUTO-GENERATED
|
||||
↓ (import)
|
||||
**API Definitions** (`flight.js`, etc.) - Reference constants
|
||||
↓ (use)
|
||||
**Transpiler & Decompiler** - Use constants
|
||||
|
||||
## Usage
|
||||
|
||||
### Generate Constants
|
||||
|
||||
```bash
|
||||
node scripts/generate-constants.js <path-to-logic_condition.h> [output-path]
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
# From INAV configurator root
|
||||
node scripts/generate-constants.js \
|
||||
../inav/src/main/programming/logic_condition.h \
|
||||
js/transpiler/transpiler/inav_constants.js
|
||||
```
|
||||
|
||||
### Default Output
|
||||
|
||||
If output path is not specified, defaults to:
|
||||
```
|
||||
js/transpiler/transpiler/inav_constants.js
|
||||
```
|
||||
|
||||
## What It Parses
|
||||
|
||||
The parser extracts these enums from `logic_condition.h`:
|
||||
|
||||
1. **logicOperation_e** → `OPERATION`
|
||||
- TRUE, EQUAL, GREATER_THAN, etc.
|
||||
|
||||
2. **logicOperandType_s** → `OPERAND_TYPE`
|
||||
- VALUE, RC_CHANNEL, FLIGHT, etc.
|
||||
|
||||
3. **logicFlightOperands_e** → `FLIGHT_PARAM`
|
||||
- ARM_TIMER, HOME_DISTANCE, RSSI, YAW, etc.
|
||||
|
||||
4. **logicFlightModeOperands_e** → `FLIGHT_MODE`
|
||||
- FAILSAFE, MANUAL, RTH, etc.
|
||||
|
||||
5. **logicWaypointOperands_e** → `WAYPOINT_PARAM`
|
||||
- IS_WP, WAYPOINT_INDEX, etc.
|
||||
|
||||
## Generated File Format
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* AUTO-GENERATED from firmware header files
|
||||
* DO NOT EDIT MANUALLY
|
||||
*/
|
||||
|
||||
const OPERAND_TYPE = {
|
||||
VALUE: 0,
|
||||
RC_CHANNEL: 1,
|
||||
FLIGHT: 2,
|
||||
// ...
|
||||
};
|
||||
|
||||
const OPERATION = {
|
||||
TRUE: 0,
|
||||
EQUAL: 1,
|
||||
// ...
|
||||
};
|
||||
|
||||
const FLIGHT_PARAM = {
|
||||
ARM_TIMER: 0,
|
||||
HOME_DISTANCE: 1,
|
||||
// ...
|
||||
YAW: 40, // ← Correct value from firmware!
|
||||
// ...
|
||||
};
|
||||
|
||||
// ... exports
|
||||
|
||||
```
|
||||
|
||||
## Build Integration
|
||||
|
||||
Add to package.json scripts:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"generate-constants": "node scripts/generate-constants.js ../inav/src/main/programming/logic_condition.h",
|
||||
"prebuild": "npm run generate-constants"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This ensures constants are regenerated before each build.
|
||||
|
||||
## Next Steps: Update API Definitions
|
||||
|
||||
Currently, API definitions have hardcoded values:
|
||||
|
||||
```javascript
|
||||
// inav.flight.js - WRONG (hardcoded)
|
||||
yaw: {
|
||||
inavOperand: { type: 2, value: 17 } // Wrong value!
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Change to reference constants:
|
||||
|
||||
```javascript
|
||||
// inav.flight.js - CORRECT (references constants)
|
||||
const { OPERAND_TYPE, FLIGHT_PARAM } = require('../../transpiler/inav_constants.js');
|
||||
|
||||
yaw: {
|
||||
inavOperand: { type: OPERAND_TYPE.FLIGHT, value: FLIGHT_PARAM.ATTITUDE_YAW }
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Benefits:
|
||||
- ✅ Single source of truth (firmware)
|
||||
- ✅ Type-safe references
|
||||
- ✅ Compile-time errors if constants missing
|
||||
- ✅ Auto-update when firmware changes
|
||||
|
||||
## Verification
|
||||
|
||||
After generating, verify key values:
|
||||
|
||||
```bash
|
||||
grep "ATTITUDE_YAW" js/transpiler/transpiler/inav_constants.js
|
||||
# Should show: ATTITUDE_YAW: 40,
|
||||
|
||||
grep "IS_ARMED" js/transpiler/transpiler/inav_constants.js
|
||||
# Should show: IS_ARMED: 17,
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
The parser handles:
|
||||
- ✅ Both `typedef enum { } name_e;` and `typedef enum name { }` formats
|
||||
- ✅ Explicit values (`= 40`)
|
||||
- ✅ Auto-incrementing values
|
||||
- ✅ Hex values (`= 0x10`)
|
||||
- ✅ C-style comments (`//` and `/* */`)
|
||||
- ✅ Missing enums (warns but continues)
|
||||
|
||||
## Maintenance
|
||||
|
||||
**When INAV firmware updates:**
|
||||
1. Get new `logic_condition.h` from firmware repo
|
||||
2. Run `npm run generate-constants`
|
||||
3. Review diff in `inav_constants.js`
|
||||
4. Test transpiler/decompiler
|
||||
5. Commit updated constants file
|
||||
|
||||
**DO NOT manually edit `inav_constants.js`** - all changes will be overwritten!
|
||||
@@ -0,0 +1,321 @@
|
||||
# INAV JavaScript Programming - Quick Reference
|
||||
|
||||
## Relationship to Logic Conditions
|
||||
|
||||
This JavaScript programming interface is built on top of INAV's traditional
|
||||
[Logic Conditions](../../Programming%20Framework.md) system. The JavaScript code you
|
||||
write is transpiled (converted) into logic conditions that run on the flight controller.
|
||||
|
||||
If you're familiar with the traditional logic conditions interface, you can think of
|
||||
JavaScript programming as a more user-friendly syntax that generates the same logic
|
||||
conditions behind the scenes.
|
||||
|
||||
**See also:**
|
||||
- [Programming Framework documentation](../../Programming%20Framework.md) - Details about the underlying logic conditions system
|
||||
- [Operations Reference](OPERATIONS_REFERENCE.md) - Complete reference for all supported operations
|
||||
|
||||
---
|
||||
|
||||
## Pattern Guide
|
||||
|
||||
### Continuous Conditions (if statements)
|
||||
Use `if` statements for conditions that should check and execute **every cycle**:
|
||||
|
||||
```javascript
|
||||
|
||||
// Checks every cycle - adjusts VTX power continuously
|
||||
if (inav.flight.homeDistance > 100) {
|
||||
inav.override.vtx.power = 3;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
**Use when:** You want the action to happen continuously while the condition is true.
|
||||
|
||||
---
|
||||
|
||||
### One-Time Execution (edge)
|
||||
Use `edge()` for actions that should execute **only once** when a condition becomes true:
|
||||
|
||||
```javascript
|
||||
|
||||
// Executes ONCE when armTimer reaches 1000ms
|
||||
inav.events.edge(() => inav.flight.armTimer > 1000, { duration: 0 }, () => {
|
||||
inav.gvar[0] = inav.flight.yaw; // Save initial heading
|
||||
inav.gvar[1] = 0; // Initialize counter
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- **condition**: Function returning boolean
|
||||
- **duration**: Minimum duration in ms (0 = instant, >0 = debounce)
|
||||
- **action**: Function to execute once
|
||||
|
||||
**Use when:**
|
||||
- Initializing on arm
|
||||
- Detecting events (first time RSSI drops)
|
||||
- Counting discrete occurrences
|
||||
- Debouncing noisy signals
|
||||
|
||||
---
|
||||
|
||||
### Latching/Sticky Conditions
|
||||
Use `sticky()` for conditions that latch ON and stay ON until reset:
|
||||
|
||||
```javascript
|
||||
|
||||
// Latches ON when RSSI < 30, stays ON until RSSI > 70
|
||||
inav.events.sticky(
|
||||
() => inav.flight.rssi < 30, // ON condition
|
||||
() => inav.flight.rssi > 70, // OFF condition
|
||||
() => {
|
||||
inav.override.vtx.power = 4; // Executes while latched
|
||||
}
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- **onCondition**: When to latch ON
|
||||
- **offCondition**: When to latch OFF
|
||||
- **action**: What to do while latched
|
||||
|
||||
**Use when:**
|
||||
- Warning states that need manual reset
|
||||
- Hysteresis/deadband behavior
|
||||
- Failsafe conditions
|
||||
|
||||
---
|
||||
|
||||
### Delayed Execution (delay)
|
||||
Use `delay()` to execute after a condition has been true for a duration:
|
||||
|
||||
```javascript
|
||||
|
||||
// Executes only if RSSI < 30 for 2 seconds continuously
|
||||
inav.events.delay(() => inav.flight.rssi < 30, { duration: 2000 }, () => {
|
||||
inav.gvar[0] = 1; // Set failsafe flag
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- **condition**: Condition that must remain true
|
||||
- **duration**: How long condition must be true (ms)
|
||||
- **action**: Action to execute after delay
|
||||
|
||||
**Use when:**
|
||||
- Avoiding false triggers
|
||||
- Requiring sustained conditions
|
||||
- Timeouts and delays
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Initialize on Arm
|
||||
```javascript
|
||||
|
||||
inav.events.edge(() => inav.flight.armTimer > 1000, { duration: 0 }, () => {
|
||||
inav.gvar[0] = 0; // Reset counter
|
||||
inav.gvar[1] = inav.flight.yaw; // Save heading
|
||||
inav.gvar[2] = inav.flight.altitude; // Save starting altitude
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Count Events
|
||||
```javascript
|
||||
|
||||
// Initialize
|
||||
inav.events.edge(() => inav.flight.armTimer > 1000, { duration: 0 }, () => {
|
||||
inav.gvar[0] = 0;
|
||||
});
|
||||
|
||||
// Count each time RSSI drops below 30 (counts transitions, not duration)
|
||||
inav.events.edge(() => inav.flight.rssi < 30, { duration: 100 }, () => {
|
||||
inav.gvar[0] = inav.gvar[0] + 1;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Debounce Noisy Signals
|
||||
```javascript
|
||||
|
||||
// Only trigger if RSSI < 30 for at least 500ms
|
||||
inav.events.edge(() => inav.flight.rssi < 30, { duration: 500 }, () => {
|
||||
inav.override.vtx.power = 4;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Multi-Stage Logic
|
||||
```javascript
|
||||
|
||||
// Stage 1: Far away
|
||||
if (inav.flight.homeDistance > 500) {
|
||||
inav.override.vtx.power = 4;
|
||||
}
|
||||
|
||||
// Stage 2: Medium distance
|
||||
if (inav.flight.homeDistance > 200 && inav.flight.homeDistance <= 500) {
|
||||
inav.override.vtx.power = 3;
|
||||
}
|
||||
|
||||
// Stage 3: Close to home
|
||||
if (inav.flight.homeDistance <= 200) {
|
||||
inav.override.vtx.power = 2;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Hysteresis/Deadband
|
||||
```javascript
|
||||
|
||||
// Turn ON at low voltage, turn OFF when recovered
|
||||
inav.events.sticky(
|
||||
() => inav.flight.cellVoltage < 330, // Warning threshold
|
||||
() => inav.flight.cellVoltage > 350, // Recovery threshold
|
||||
() => {
|
||||
inav.override.throttleScale = 50; // Reduce throttle while in warning
|
||||
inav.gvar[0] = 1; // Warning flag
|
||||
}
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Differences
|
||||
|
||||
| Pattern | Executes | Reset | Use Case |
|
||||
|---------|----------|-------|----------|
|
||||
| `if` | Every cycle while true | N/A | Continuous control |
|
||||
| `edge()` | Once per transition | When condition becomes false | Events, initialization |
|
||||
| `sticky()` | Continuous while latched | When OFF condition met | Warnings, hysteresis |
|
||||
| `delay()` | Once after duration | When condition becomes false | Timeouts, debouncing |
|
||||
|
||||
---
|
||||
|
||||
## Variables
|
||||
|
||||
### Let/Const Variables
|
||||
|
||||
Use `let` or `const` to define reusable expressions that are compiled into the logic:
|
||||
|
||||
```javascript
|
||||
// Define reusable calculations
|
||||
let distanceThreshold = 500;
|
||||
let altitudeLimit = 100;
|
||||
let combinedCondition = inav.flight.homeDistance > distanceThreshold && inav.flight.altitude > altitudeLimit;
|
||||
|
||||
// Use in conditions
|
||||
if (combinedCondition) {
|
||||
inav.override.vtx.power = 4;
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Makes code more readable with named values
|
||||
- Compiler automatically optimizes duplicate expressions
|
||||
- Variables preserve their custom names through compile/decompile cycles
|
||||
|
||||
**Important:** `let`/`const` variables are **compile-time substituted**, not runtime variables. For runtime state, use `inav.gvar[]`.
|
||||
|
||||
### Ternary Operator
|
||||
|
||||
Use ternary expressions for conditional values:
|
||||
|
||||
```javascript
|
||||
// Assign based on condition
|
||||
let throttleLimit = inav.flight.cellVoltage < 330 ? 25 : 50;
|
||||
|
||||
if (inav.flight.cellVoltage < 350) {
|
||||
inav.override.throttleScale = throttleLimit;
|
||||
}
|
||||
|
||||
// Inline in expressions
|
||||
inav.override.vtx.power = inav.flight.homeDistance > 500 ? 4 : 2;
|
||||
```
|
||||
|
||||
**Use when:** You need conditional value assignment in a single expression.
|
||||
|
||||
---
|
||||
|
||||
## Available Objects
|
||||
|
||||
The `inav` namespace provides access to all flight controller data and control functions:
|
||||
|
||||
- `inav.flight` - Flight telemetry (including `flight.mode.*`)
|
||||
- `inav.override` - Override flight parameters
|
||||
- `inav.rc` - RC channels
|
||||
- `inav.gvar` - Global variables (0-7)
|
||||
- `inav.pid` - Programming PID outputs (`pid[0-3].output`)
|
||||
- `inav.waypoint` - Waypoint navigation
|
||||
- `inav.events.edge` - Edge detection
|
||||
- `inav.events.sticky` - Latching conditions
|
||||
- `inav.events.delay` - Delayed execution
|
||||
|
||||
### Flight Mode Detection
|
||||
|
||||
Check which flight modes are currently active via `inav.flight.mode.*`:
|
||||
|
||||
```javascript
|
||||
if (inav.flight.mode.poshold === 1) {
|
||||
inav.gvar[0] = 1; // Flag: in position hold
|
||||
}
|
||||
|
||||
if (inav.flight.mode.rth === 1) {
|
||||
inav.override.vtx.power = 4; // Max power during RTH
|
||||
}
|
||||
```
|
||||
|
||||
**Available modes:** `failsafe`, `manual`, `rth`, `poshold`, `cruise`, `althold`, `angle`, `horizon`, `air`, `acro`, `courseHold`, `waypointMission`, `user1` through `user4`
|
||||
|
||||
### PID Controller Outputs
|
||||
|
||||
Read output values from the 4 programming PID controllers (configured in Programming PID tab):
|
||||
|
||||
```javascript
|
||||
if (inav.pid[0].output > 500) {
|
||||
inav.override.throttle = 1600;
|
||||
}
|
||||
|
||||
inav.gvar[0] = inav.pid[0].output; // Store for OSD display
|
||||
```
|
||||
|
||||
**Available:** `inav.pid[0].output` through `inav.pid[3].output`
|
||||
|
||||
---
|
||||
|
||||
## Tips
|
||||
|
||||
1. **Initialize variables on arm** using `inav.events.edge()` with `inav.flight.armTimer > 1000`
|
||||
2. **Use inav.gvar for state** - they persist between logic condition evaluations
|
||||
3. **edge() duration = 0** means instant trigger on condition becoming true
|
||||
4. **edge() duration > 0** adds debounce time
|
||||
5. **if statements are continuous** - they execute every cycle
|
||||
6. **sticky() provides hysteresis** - prevents rapid ON/OFF switching
|
||||
7. **Use Math functions** - `Math.abs()`, `Math.min()`, `Math.max()` are available
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
Use global variables to track state:
|
||||
```javascript
|
||||
|
||||
// Debug counter
|
||||
inav.events.edge(() => inav.flight.armTimer > 1000, { duration: 0 }, () => {
|
||||
inav.gvar[7] = 0; // Use inav.gvar[7] as debug counter
|
||||
});
|
||||
|
||||
// Increment on each event
|
||||
inav.events.edge(() => inav.flight.rssi < 30, { duration: 0 }, () => {
|
||||
inav.gvar[7] = inav.gvar[7] + 1;
|
||||
});
|
||||
|
||||
// Check inav.gvar[7] value in OSD or Configurator to see event count
|
||||
|
||||
```
|
||||
@@ -0,0 +1,126 @@
|
||||
# INAV Logic Condition Operations Reference
|
||||
|
||||
This document provides a complete reference for all INAV logic condition operations and their JavaScript transpiler implementations.
|
||||
|
||||
## All INAV Operations Now Implemented! 🎉
|
||||
|
||||
All INAV logic condition operations supported by the firmware are now fully implemented in the JavaScript transpiler.
|
||||
|
||||
## Already Implemented
|
||||
|
||||
✅ **Arithmetic**: ADD, SUB, MUL, DIV, MODULUS (via +, -, *, /, %)
|
||||
✅ **Comparisons**: EQUAL, GREATER_THAN, LOWER_THAN, APPROX_EQUAL (via ===, >, <, approxEqual())
|
||||
✅ **Logical**: AND, OR, NOT, XOR, NAND, NOR (via &&, ||, !, xor(), nand(), nor())
|
||||
✅ **Math**: MIN, MAX, SIN, COS, TAN, ABS (via Math.min/max/sin/cos/tan/abs)
|
||||
✅ **Scaling**: MAP_INPUT, MAP_OUTPUT (via mapInput(), mapOutput())
|
||||
✅ **Flow control**: STICKY, EDGE, DELAY, TIMER, DELTA (via on.* and helper functions)
|
||||
✅ **Variables**: GVAR_SET, GVAR_INC, GVAR_DEC (via assignments, ++, --)
|
||||
✅ **Overrides**: VTX, throttle, arming, etc. (via override.* properties)
|
||||
✅ **RC Channel States**: LOW, MID, HIGH (via rc[n].low, rc[n].mid, rc[n].high)
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Logical Operations
|
||||
|
||||
```javascript
|
||||
// XOR - true when exactly one condition is true
|
||||
if (xor(inav.flight.armed, inav.flight.mode.failsafe)) {
|
||||
// One or the other, but not both
|
||||
}
|
||||
|
||||
// NAND - false only when both are true
|
||||
if (nand(inav.flight.armed, inav.gvar[0] > 100)) {
|
||||
// Not both conditions true
|
||||
}
|
||||
|
||||
// NOR - true only when both are false
|
||||
if (nor(inav.flight.mode.failsafe, inav.flight.mode.rth)) {
|
||||
// Neither failsafe nor RTH active
|
||||
}
|
||||
|
||||
// Approximate equality with tolerance
|
||||
if (approxEqual(inav.flight.altitude, 1000, 50)) {
|
||||
// Altitude is 1000 ± 50
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Scaling/Mapping Operations
|
||||
|
||||
```javascript
|
||||
// mapInput: Scale from [0:maxValue] to [0:1000]
|
||||
// Example: RC value (1000-2000) to normalized (0-1000)
|
||||
const normalizedThrottle = mapInput(inav.rc[3].value - 1000, 1000);
|
||||
|
||||
// mapOutput: Scale from [0:1000] to [0:maxValue]
|
||||
// Example: normalized (0-1000) to servo angle (0-180)
|
||||
const servoAngle = mapOutput(normalizedThrottle, 180);
|
||||
|
||||
// Chaining for full range mapping
|
||||
// Map altitude (0-5000m) to percentage (0-100)
|
||||
const altitudePercent = mapOutput(mapInput(inav.flight.altitude, 5000), 100);
|
||||
|
||||
```
|
||||
|
||||
### RC Channel State Detection
|
||||
|
||||
```javascript
|
||||
// LOW state - RC value < 1333us
|
||||
if (inav.rc[0].low) {
|
||||
// Roll stick is in low position
|
||||
inav.gvar[0] = 1;
|
||||
}
|
||||
|
||||
// MID state - RC value between 1333-1666us
|
||||
if (inav.rc[1].mid) {
|
||||
// Pitch stick is centered
|
||||
inav.gvar[1] = 1;
|
||||
}
|
||||
|
||||
// HIGH state - RC value > 1666us
|
||||
if (inav.rc[2].high) {
|
||||
// Throttle stick is in high position
|
||||
inav.override.vtx.power = 4;
|
||||
}
|
||||
|
||||
// Access raw RC value
|
||||
if (inav.rc[3].value > 1700) {
|
||||
// Custom threshold on yaw channel
|
||||
inav.gvar[2] = 1;
|
||||
}
|
||||
|
||||
// Combined with logical operations
|
||||
if (inav.rc[0].low && inav.rc[1].mid && inav.rc[2].high) {
|
||||
// Specific stick combination detected
|
||||
inav.override.armSafety = 1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- RC channels: `rc[0]` through `rc[17]` (18 channels)
|
||||
- RC channel properties: `.value` (1000-2000us), `.low` (<1333us), `.mid` (1333-1666us), `.high` (>1666us)
|
||||
- All trig functions (sin/cos/tan) take degrees, not radians
|
||||
- MODULUS operator bug fixed: `%` now correctly generates MODULUS operation
|
||||
- MAP_INPUT normalizes to [0:1000], MAP_OUTPUT scales from [0:1000]
|
||||
|
||||
## Recent Changes
|
||||
|
||||
**2025-11-25 (Session 3)**:
|
||||
- ✅ Implemented RC channel LOW/MID/HIGH state detection (rc[n].low, rc[n].mid, rc[n].high)
|
||||
- ✅ Fixed analyzer to validate RC channel array access patterns
|
||||
- ✅ Updated codegen to generate LOW/MID/HIGH operations (operations 4, 5, 6)
|
||||
- 🎉 ALL INAV logic condition operations now fully implemented and tested!
|
||||
|
||||
**2025-11-25 (Session 2)**:
|
||||
- ✅ Implemented XOR, NAND, NOR logical operations (xor(), nand(), nor())
|
||||
- ✅ Implemented APPROX_EQUAL comparison (approxEqual())
|
||||
- ✅ Implemented MAP_INPUT and MAP_OUTPUT scaling (mapInput(), mapOutput())
|
||||
|
||||
**2025-11-25 (Session 1)**:
|
||||
- ✅ Implemented Math.min(), Math.max(), Math.sin(), Math.cos(), Math.tan()
|
||||
- ✅ Fixed MODULUS operator (was using wrong constant name)
|
||||
- 📝 Documented unimplemented operations for future work
|
||||
|
||||
**Last Updated**: 2025-11-25
|
||||
@@ -0,0 +1,267 @@
|
||||
# Transpiler Testing Guide
|
||||
|
||||
This guide explains how to test changes to the INAV JavaScript transpiler to ensure all components work together correctly.
|
||||
|
||||
## Overview
|
||||
|
||||
The transpiler has 4 main components that must stay in sync:
|
||||
|
||||
1. **Parser** (`transpiler/parser.js`) - Parses JavaScript code into AST
|
||||
2. **Analyzer** (`transpiler/analyzer.js`) - Validates syntax and checks for errors
|
||||
3. **Codegen** (`transpiler/codegen.js`) - Generates INAV CLI commands from AST
|
||||
4. **Decompiler** (`transpiler/decompiler.js`) - Converts CLI commands back to JavaScript
|
||||
|
||||
## When Adding a New Feature
|
||||
|
||||
When implementing a new operation, function, or syntax feature, you must update:
|
||||
|
||||
### 1. Analyzer (transpiler/analyzer.js)
|
||||
- Add validation for new syntax patterns
|
||||
- Update property access validation if needed
|
||||
- Add warnings for unsupported variations
|
||||
|
||||
Example: For RC channel states, added validation for `rc[0-17]` with `.low/.mid/.high` properties
|
||||
|
||||
### 2. Codegen (transpiler/codegen.js)
|
||||
- Add code generation for the new feature
|
||||
- Handle the new AST node types
|
||||
- Generate correct INAV operation codes
|
||||
|
||||
Example: For RC channel states, added handler in `generateCondition()` to detect `rc[n].low` and generate LOW operation (4)
|
||||
|
||||
### 3. Decompiler (transpiler/decompiler.js)
|
||||
- Add reverse mapping from operation codes to JavaScript
|
||||
- Ensure generated code matches expected syntax
|
||||
|
||||
Example: For RC channel states, map operations 4/5/6 to `.low/.mid/.high` properties
|
||||
|
||||
### 4. Diagnostics (optional, editor/diagnostics.js)
|
||||
- Add helpful warnings for common mistakes
|
||||
- Suggest correct syntax alternatives
|
||||
|
||||
## Round-Trip Testing
|
||||
|
||||
The most reliable way to test transpiler changes is round-trip testing:
|
||||
JavaScript → CLI commands → JavaScript
|
||||
|
||||
### Basic Test Template
|
||||
|
||||
```javascript
|
||||
import { Transpiler } from './transpiler/index.js';
|
||||
import { Decompiler } from './transpiler/decompiler.js';
|
||||
|
||||
const testCode = `
|
||||
|
||||
// Your test code here
|
||||
if (inav.flight.altitude > 100) {
|
||||
inav.gvar[0] = 1;
|
||||
}
|
||||
`;
|
||||
|
||||
console.log('=== Original JavaScript ===\n');
|
||||
console.log(testCode);
|
||||
|
||||
// Transpile
|
||||
const transpiler = new Transpiler();
|
||||
const transpileResult = transpiler.transpile(testCode);
|
||||
|
||||
console.log('\n=== Generated CLI Commands ===\n');
|
||||
transpileResult.commands.forEach(cmd => console.log(cmd));
|
||||
|
||||
// Parse commands to LC format for decompiler
|
||||
const logicConditions = transpileResult.commands.map((cmd) => {
|
||||
const parts = cmd.split(/\s+/);
|
||||
return {
|
||||
index: parseInt(parts[1]),
|
||||
enabled: parseInt(parts[2]),
|
||||
activatorId: parseInt(parts[3]),
|
||||
operation: parseInt(parts[4]),
|
||||
operandAType: parseInt(parts[5]),
|
||||
operandAValue: parseInt(parts[6]),
|
||||
operandBType: parseInt(parts[7]),
|
||||
operandBValue: parseInt(parts[8]),
|
||||
flags: parseInt(parts[9])
|
||||
};
|
||||
});
|
||||
|
||||
// Decompile
|
||||
const decompiler = new Decompiler();
|
||||
const decompileResult = decompiler.decompile(logicConditions);
|
||||
|
||||
console.log('\n=== Decompiled JavaScript ===\n');
|
||||
console.log(decompileResult.code);
|
||||
|
||||
if (decompileResult.success) {
|
||||
console.log('\n✅ Round-trip successful!');
|
||||
} else {
|
||||
console.error('\n❌ Decompilation failed:', decompileResult.error);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
cd /path/to/inav-configurator/js/transpiler
|
||||
|
||||
# Create your test file
|
||||
nano test_feature.js
|
||||
|
||||
# Run the test
|
||||
node test_feature.js
|
||||
|
||||
# Clean up after testing
|
||||
rm test_feature.js
|
||||
```
|
||||
|
||||
## Test Cases to Verify
|
||||
|
||||
When making changes, test these scenarios:
|
||||
|
||||
### 1. Basic Functionality
|
||||
```javascript
|
||||
|
||||
if (inav.flight.altitude > 100) {
|
||||
inav.gvar[0] = 1;
|
||||
}
|
||||
|
||||
```
|
||||
Expected: 2 commands (condition + action)
|
||||
|
||||
### 2. Error Handling
|
||||
```javascript
|
||||
// Invalid syntax - should produce helpful error
|
||||
if (inav.flight.invalidProperty > 100) {
|
||||
inav.gvar[0] = 1;
|
||||
}
|
||||
|
||||
```
|
||||
Expected: Error with suggestion for correct property
|
||||
|
||||
### 3. Edge Cases
|
||||
```javascript
|
||||
// Test boundary values
|
||||
if (inav.rc[0].low) { // Channel 0 (first)
|
||||
inav.gvar[0] = 1;
|
||||
}
|
||||
|
||||
if (inav.rc[17].high) { // Channel 17 (last valid)
|
||||
inav.gvar[1] = 1;
|
||||
}
|
||||
|
||||
```
|
||||
Expected: Valid generation for both
|
||||
|
||||
### 4. Complex Combinations
|
||||
```javascript
|
||||
// Test multiple operations together
|
||||
if (xor(inav.rc[0].low, inav.flight.armed)) {
|
||||
inav.gvar[0] = Math.max(100, inav.flight.altitude);
|
||||
}
|
||||
|
||||
```
|
||||
Expected: Proper nesting of operations
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Analyzer Rejects Valid Syntax
|
||||
**Problem**: Analyzer validation is too strict
|
||||
**Solution**: Update `checkPropertyAccess()` or relevant validation method
|
||||
**Example**: RC channels needed special handling for array syntax `rc[n]`
|
||||
|
||||
### Codegen Produces Wrong Operation Code
|
||||
**Problem**: Operation constant name incorrect or not imported
|
||||
**Solution**: Check `OPERATION.*` constants match `inav_constants.js`
|
||||
**Example**: MODULUS was incorrectly `OPERATION.MOD` instead of `OPERATION.MODULUS`
|
||||
|
||||
### Decompiler Output Doesn't Match Input
|
||||
**Problem**: Decompiler reverse mapping incomplete
|
||||
**Solution**: Add case for new operation in decompiler
|
||||
**Example**: LOW/MID/HIGH operations needed to map to `.low/.mid/.high` properties
|
||||
|
||||
### Line Numbers Off in Errors
|
||||
**Problem**: Auto-import adds lines before parsing
|
||||
**Solution**: Track lineOffset and adjust all error/warning line numbers
|
||||
**Fixed**: Session 1 of 2025-11-25
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
Before committing changes:
|
||||
|
||||
- [ ] Analyzer validates new syntax without false positives
|
||||
- [ ] Codegen generates correct operation codes
|
||||
- [ ] Decompiler correctly reverses the operation
|
||||
- [ ] Round-trip test passes (JS → CLI → JS)
|
||||
- [ ] Error messages are helpful and accurate
|
||||
- [ ] Edge cases are handled (boundary values, empty inputs)
|
||||
- [ ] Documentation updated (OPERATIONS_REFERENCE.md, API definitions)
|
||||
|
||||
## Example: Adding a New Operation
|
||||
|
||||
Let's say you want to add support for a new INAV operation `FOOBAR (operation 99)`:
|
||||
|
||||
**1. Check if operation exists in firmware**
|
||||
```javascript
|
||||
// Look in transpiler/inav_constants.js
|
||||
const OPERATION = {
|
||||
// ...
|
||||
FOOBAR: 99, // Check this exists
|
||||
// ...
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
**2. Update Codegen**
|
||||
```javascript
|
||||
// In transpiler/codegen.js
|
||||
case 'CallExpression': {
|
||||
const funcName = condition.callee?.name;
|
||||
|
||||
if (funcName === 'foobar') {
|
||||
// Generate FOOBAR operation
|
||||
const resultIndex = this.lcIndex;
|
||||
this.commands.push(
|
||||
`logic ${this.lcIndex} 1 ${activatorId} ${OPERATION.FOOBAR} ...`
|
||||
);
|
||||
this.lcIndex++;
|
||||
return resultIndex;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
**3. Update Decompiler**
|
||||
```javascript
|
||||
// In transpiler/decompiler.js
|
||||
case OPERATION.FOOBAR:
|
||||
return 'foobar()';
|
||||
|
||||
```
|
||||
|
||||
**4. Update Analyzer (if needed)**
|
||||
```javascript
|
||||
// In transpiler/analyzer.js
|
||||
// Add validation for foobar() usage
|
||||
|
||||
```
|
||||
|
||||
**5. Test Round-Trip**
|
||||
```javascript
|
||||
const testCode = `
|
||||
|
||||
if (foobar()) {
|
||||
inav.gvar[0] = 1;
|
||||
}
|
||||
`;
|
||||
// Run round-trip test...
|
||||
|
||||
```
|
||||
|
||||
**6. Update Documentation**
|
||||
- Add to OPERATIONS_REFERENCE.md
|
||||
- Add usage examples
|
||||
- Update API definitions if needed
|
||||
|
||||
## Last Updated
|
||||
|
||||
2025-11-25 - Created after implementing RC channel state detection and completing all INAV operations
|
||||
@@ -0,0 +1,297 @@
|
||||
# timer() and whenChanged() Examples
|
||||
|
||||
## timer() Examples
|
||||
|
||||
### Example 1: Periodic VTX Power Boost
|
||||
```javascript
|
||||
|
||||
// Boost VTX power to maximum for 1 second every 5 seconds
|
||||
inav.events.timer(1000, 5000, () => {
|
||||
inav.override.vtx.power = 4;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Example 2: Flashing OSD Layout
|
||||
```javascript
|
||||
|
||||
// Alternate between OSD layout 0 and 1 every second
|
||||
inav.events.timer(1000, 1000, () => {
|
||||
inav.override.osdLayout = 1;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Example 3: Periodic Status Check
|
||||
```javascript
|
||||
|
||||
// Record battery voltage every 10 seconds for 100ms
|
||||
inav.events.timer(100, 10000, () => {
|
||||
inav.gvar[0] = inav.flight.vbat;
|
||||
inav.gvar[1] = inav.flight.current;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Example 4: Warning Beep Pattern
|
||||
```javascript
|
||||
|
||||
// Beep pattern: 200ms on, 200ms off, 200ms on, 5s off
|
||||
// (Would need multiple timers or different approach for complex patterns)
|
||||
inav.events.timer(200, 200, () => {
|
||||
inav.override.rcChannel(8, 2000); // Beeper channel
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
## whenChanged() Examples
|
||||
|
||||
### Example 1: Altitude Change Logger
|
||||
```javascript
|
||||
|
||||
// Log altitude whenever it changes by 50cm or more
|
||||
inav.events.whenChanged(inav.flight.altitude, 50, () => {
|
||||
inav.gvar[0] = inav.flight.altitude;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Example 2: RSSI Drop Detection
|
||||
```javascript
|
||||
|
||||
// Boost VTX power when RSSI drops by 10 or more
|
||||
inav.events.whenChanged(inav.flight.rssi, 10, () => {
|
||||
if (inav.flight.rssi < 50) {
|
||||
inav.override.vtx.power = 4;
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Example 3: Speed Change Tracker
|
||||
```javascript
|
||||
|
||||
// Track ground speed changes of 100cm/s or more
|
||||
inav.events.whenChanged(inav.flight.groundSpeed, 100, () => {
|
||||
inav.gvar[1] = inav.flight.groundSpeed;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Example 4: Battery Voltage Monitor
|
||||
```javascript
|
||||
|
||||
// Record voltage whenever it changes by 1 unit (0.1V)
|
||||
inav.events.whenChanged(inav.flight.vbat, 1, () => {
|
||||
inav.gvar[2] = inav.flight.vbat;
|
||||
inav.gvar[3] = inav.flight.mahDrawn;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Example 5: Climb Rate Detection
|
||||
```javascript
|
||||
|
||||
// Detect rapid climbs (>50cm/s change in vertical speed)
|
||||
inav.events.whenChanged(inav.flight.verticalSpeed, 50, () => {
|
||||
if (inav.flight.verticalSpeed > 200) {
|
||||
// Climbing fast - reduce throttle scale
|
||||
inav.override.throttleScale = 80;
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
## Combined Examples
|
||||
|
||||
### Example 1: Timer + WhenChanged
|
||||
```javascript
|
||||
|
||||
// Periodic VTX boost
|
||||
inav.events.timer(1000, 5000, () => {
|
||||
inav.override.vtx.power = 4;
|
||||
});
|
||||
|
||||
// Log altitude changes
|
||||
inav.events.whenChanged(inav.flight.altitude, 100, () => {
|
||||
inav.gvar[0] = inav.flight.altitude;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Example 2: Conditional Timer
|
||||
```javascript
|
||||
|
||||
// Only run timer when armed
|
||||
if (inav.flight.isArmed) {
|
||||
inav.events.timer(500, 500, () => {
|
||||
inav.override.osdLayout = 2;
|
||||
});
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Example 3: Emergency Response System
|
||||
```javascript
|
||||
|
||||
// Monitor RSSI drops
|
||||
inav.events.whenChanged(inav.flight.rssi, 5, () => {
|
||||
if (inav.flight.rssi < 30) {
|
||||
// RSSI critical - max VTX power
|
||||
inav.override.vtx.power = 4;
|
||||
inav.gvar[7] = 1; // Set emergency flag
|
||||
}
|
||||
});
|
||||
|
||||
// Monitor altitude changes
|
||||
inav.events.whenChanged(inav.flight.altitude, 200, () => {
|
||||
if (inav.flight.altitude > 10000) {
|
||||
// Too high - warn
|
||||
inav.gvar[6] = inav.flight.altitude;
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Example 4: Data Logging System
|
||||
```javascript
|
||||
|
||||
// Periodic logging every 5 seconds
|
||||
inav.events.timer(100, 5000, () => {
|
||||
inav.gvar[0] = inav.flight.vbat;
|
||||
inav.gvar[1] = inav.flight.current;
|
||||
inav.gvar[2] = inav.flight.altitude;
|
||||
});
|
||||
|
||||
// Event-based logging on significant changes
|
||||
inav.events.whenChanged(inav.flight.altitude, 500, () => {
|
||||
inav.gvar[3] = inav.flight.altitude;
|
||||
inav.gvar[4] = inav.flight.verticalSpeed;
|
||||
});
|
||||
|
||||
inav.events.whenChanged(inav.flight.groundSpeed, 200, () => {
|
||||
inav.gvar[5] = inav.flight.groundSpeed;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
## Logic Condition Output Examples
|
||||
|
||||
### timer() Output
|
||||
```javascript
|
||||
inav.events.timer(1000, 2000, () => {
|
||||
inav.gvar[0] = 1;
|
||||
});
|
||||
|
||||
```
|
||||
Generates:
|
||||
```
|
||||
logic 0 1 -1 49 0 1000 0 2000 0 # TIMER: 1000ms ON, 2000ms OFF
|
||||
logic 1 1 0 18 0 0 0 0 1 0 # gvar[0] = 1
|
||||
```
|
||||
|
||||
### whenChanged() Output
|
||||
```javascript
|
||||
inav.events.whenChanged(inav.flight.altitude, 50, () => {
|
||||
inav.gvar[0] = inav.flight.altitude;
|
||||
});
|
||||
|
||||
```
|
||||
Generates:
|
||||
```
|
||||
logic 0 1 -1 50 2 12 0 50 0 # DELTA: altitude, threshold 50
|
||||
logic 1 1 0 18 0 0 2 12 0 # gvar[0] = altitude
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### timer() Use Cases
|
||||
1. **Periodic Tasks** - Execute actions at regular intervals
|
||||
2. **Flashing Indicators** - Toggle states on/off
|
||||
3. **Sampling** - Collect data periodically
|
||||
4. **Timeouts** - Implement time-based state machines
|
||||
5. **Warning Systems** - Beep or flash warnings
|
||||
|
||||
### whenChanged() Use Cases
|
||||
1. **Event Detection** - React to significant changes
|
||||
2. **Data Logging** - Record values when they change
|
||||
3. **Thresholds** - Trigger actions on large changes
|
||||
4. **Rate Limiting** - Avoid excessive updates (100ms window)
|
||||
5. **Monitoring** - Track important parameter changes
|
||||
|
||||
## Best Practices
|
||||
|
||||
### timer()
|
||||
- ✅ Use for periodic, time-based actions
|
||||
- ✅ Keep durations reasonable (>100ms)
|
||||
- ✅ Avoid very short ON times (may miss cycles)
|
||||
- ❌ Don't use for one-time delays (use delay() instead)
|
||||
- ❌ Don't nest timers (creates complex behavior)
|
||||
|
||||
### whenChanged()
|
||||
- ✅ Use for event-driven responses
|
||||
- ✅ Set thresholds appropriate to your data
|
||||
- ✅ Monitor slowly-changing values
|
||||
- ❌ Don't use for rapidly-changing values (may trigger constantly)
|
||||
- ❌ Don't set threshold too low (noise will trigger it)
|
||||
- ⚠️ Remember: 100ms detection window (built into DELTA operation)
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Pattern 1: Status Monitor
|
||||
```javascript
|
||||
// Log key parameters when they change significantly
|
||||
inav.events.whenChanged(inav.flight.vbat, 2, () => { inav.gvar[0] = inav.flight.vbat; });
|
||||
inav.events.whenChanged(inav.flight.rssi, 10, () => { inav.gvar[1] = inav.flight.rssi; });
|
||||
inav.events.whenChanged(inav.flight.altitude, 100, () => { inav.gvar[2] = inav.flight.altitude; });
|
||||
|
||||
```
|
||||
|
||||
### Pattern 2: Periodic Beacon
|
||||
```javascript
|
||||
// Boost VTX power briefly every 10 seconds
|
||||
inav.events.timer(500, 10000, () => {
|
||||
inav.override.vtx.power = 4;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Pattern 3: Adaptive Response
|
||||
```javascript
|
||||
// React to rapid altitude changes
|
||||
inav.events.whenChanged(inav.flight.altitude, 200, () => {
|
||||
if (inav.flight.altitude < 1000) {
|
||||
inav.override.throttleScale = 120; // Boost
|
||||
} else {
|
||||
inav.override.throttleScale = 80; // Reduce
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### timer() Issues
|
||||
**Problem:** Timer doesn't seem to trigger
|
||||
- Check that ON duration > 0
|
||||
- Check that OFF duration > 0
|
||||
- Verify actions are valid
|
||||
|
||||
**Problem:** Timer seems irregular
|
||||
- INAV runs at different rates depending on mode
|
||||
- Timer precision is limited by LC execution rate
|
||||
|
||||
### whenChanged() Issues
|
||||
**Problem:** Never triggers
|
||||
- Check threshold isn't too high
|
||||
- Verify value actually changes
|
||||
- Remember: 100ms detection window
|
||||
|
||||
**Problem:** Triggers too often
|
||||
- Increase threshold
|
||||
- Value may be noisy
|
||||
- Consider using timer() for periodic sampling instead
|
||||
|
||||
**Problem:** Doesn't track fast changes
|
||||
- Built-in 100ms window
|
||||
- Use timer() for faster sampling if needed
|
||||
@@ -0,0 +1,156 @@
|
||||
# timer() and whenChanged() Functions
|
||||
|
||||
## Overview
|
||||
|
||||
The `timer()` and `whenChanged()` functions provide advanced timing and change-detection capabilities for INAV logic programming.
|
||||
|
||||
## timer() Function
|
||||
|
||||
### Syntax
|
||||
```javascript
|
||||
inav.events.timer(onMs, offMs, () => {
|
||||
// actions
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Description
|
||||
Execute actions on a periodic timer with on/off cycling. The action executes during the "on" period and pauses during the "off" period.
|
||||
|
||||
### Parameters
|
||||
- `onMs`: Duration in milliseconds to run the action
|
||||
- `offMs`: Duration in milliseconds to wait between executions
|
||||
- `action`: Arrow function containing actions to execute during on-time
|
||||
|
||||
### Example
|
||||
```javascript
|
||||
|
||||
// Flash VTX power: ON for 1 second, OFF for 2 seconds, repeat
|
||||
inav.events.timer(1000, 2000, () => {
|
||||
inav.override.vtx.power = 4;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
**Generated Logic Conditions:**
|
||||
```
|
||||
logic 0 1 -1 49 0 1000 0 2000 0 # TIMER: ON 1000ms, OFF 2000ms
|
||||
logic 1 1 0 25 0 0 0 0 4 0 # Set VTX power = 4
|
||||
```
|
||||
|
||||
### Validation Rules
|
||||
- Requires exactly 3 arguments
|
||||
- `onMs` must be numeric literal > 0
|
||||
- `offMs` must be numeric literal > 0
|
||||
- Third argument must be arrow function with actions
|
||||
|
||||
## whenChanged() Function
|
||||
|
||||
### Syntax
|
||||
```javascript
|
||||
inav.events.whenChanged(value, threshold, () => {
|
||||
// actions
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Description
|
||||
Execute actions when a monitored value changes by more than the specified threshold. Uses a built-in 100ms detection window.
|
||||
|
||||
### Parameters
|
||||
- `value`: Flight parameter or global variable to monitor
|
||||
- `threshold`: Minimum change required to trigger (numeric literal)
|
||||
- `action`: Arrow function containing actions to execute on change
|
||||
|
||||
### Example
|
||||
```javascript
|
||||
|
||||
// Log altitude whenever it changes by 50cm or more
|
||||
inav.events.whenChanged(inav.flight.altitude, 50, () => {
|
||||
inav.gvar[0] = inav.flight.altitude;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
**Generated Logic Conditions:**
|
||||
```
|
||||
logic 0 1 -1 50 2 12 0 50 0 # DELTA: altitude, threshold 50
|
||||
logic 1 1 0 18 0 0 2 12 0 # gvar[0] = altitude
|
||||
```
|
||||
|
||||
### Validation Rules
|
||||
- Requires exactly 3 arguments
|
||||
- `value` must be a valid flight parameter or gvar
|
||||
- `threshold` must be numeric literal > 0
|
||||
- Third argument must be arrow function with actions
|
||||
|
||||
## Round-Trip Support
|
||||
|
||||
Both functions support perfect round-trip transpilation/decompilation:
|
||||
|
||||
### timer() Round-Trip
|
||||
```javascript
|
||||
// Original JavaScript
|
||||
inav.events.timer(1000, 2000, () => { inav.gvar[0] = 1; });
|
||||
|
||||
// Transpiled to logic conditions
|
||||
logic 0 1 -1 49 0 1000 0 2000 0
|
||||
logic 1 1 0 18 0 0 0 0 1 0
|
||||
|
||||
// Decompiled back to JavaScript
|
||||
inav.events.timer(1000, 2000, () => {
|
||||
inav.gvar[0] = 1;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### whenChanged() Round-Trip
|
||||
```javascript
|
||||
// Original JavaScript
|
||||
inav.events.whenChanged(inav.flight.altitude, 50, () => { inav.gvar[0] = inav.flight.altitude; });
|
||||
|
||||
// Transpiled to logic conditions
|
||||
logic 0 1 -1 50 2 12 0 50 0
|
||||
logic 1 1 0 18 0 0 2 12 0
|
||||
|
||||
// Decompiled back to JavaScript
|
||||
inav.events.whenChanged(inav.flight.altitude, 50, () => {
|
||||
inav.gvar[0] = inav.flight.altitude;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
## API Definitions
|
||||
|
||||
Both functions are defined in `api/definitions/events.js`:
|
||||
|
||||
```javascript
|
||||
timer: {
|
||||
type: 'function',
|
||||
desc: 'Execute action on a periodic timer (on/off cycling)',
|
||||
params: {
|
||||
onMs: { type: 'number', unit: 'ms', desc: 'Duration to run action' },
|
||||
offMs: { type: 'number', unit: 'ms', desc: 'Duration to wait between executions' },
|
||||
action: { type: 'function', desc: 'Action to execute during on-time' }
|
||||
},
|
||||
example: 'inav.events.timer(1000, 5000, () => { inav.override.vtx.power = 4; })'
|
||||
},
|
||||
|
||||
whenChanged: {
|
||||
type: 'function',
|
||||
desc: 'Execute when value changes by more than threshold',
|
||||
params: {
|
||||
value: { type: 'number', desc: 'Value to monitor' },
|
||||
threshold: { type: 'number', desc: 'Change threshold' },
|
||||
action: { type: 'function', desc: 'Action to execute on change' }
|
||||
},
|
||||
example: 'inav.events.whenChanged(inav.flight.altitude, 100, () => { inav.gvar[0] = inav.flight.altitude; })'
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- **TIMER_WHENCHANGED_EXAMPLES.md** - More usage examples and patterns
|
||||
- **JAVASCRIPT_PROGRAMMING_GUIDE.md** - Complete programming guide
|
||||
- **events.js** - Full API definitions
|
||||
@@ -0,0 +1,284 @@
|
||||
# INAV API Definitions - Complete Implementation
|
||||
|
||||
## Overview
|
||||
|
||||
The INAV JavaScript API definitions are implemented in `js/transpiler/api/definitions/`. These files define the complete JavaScript API surface that maps to INAV firmware logic conditions.
|
||||
|
||||
## API Definition Files
|
||||
|
||||
### 1. ✅ **flight.js** - Flight Telemetry (READ-ONLY)
|
||||
**Source**: `src/main/programming/logic_condition.c` (OPERAND_FLIGHT)
|
||||
|
||||
Contains ~40 flight parameters including:
|
||||
- **Timing**: armTimer, flightTime
|
||||
- **Distance**: homeDistance, tripDistance, homeDirection
|
||||
- **Communication**: rssi
|
||||
- **Battery**: vbat, cellVoltage, current, mahDrawn, mwhDrawn, batteryPercentage
|
||||
- **GPS**: gpsSats, gpsValid
|
||||
- **Speed/Altitude**: groundSpeed, altitude, verticalSpeed
|
||||
- **Attitude**: roll, pitch, yaw, heading, throttlePos
|
||||
- **State**: isArmed, isAutoLaunch, isFailsafe
|
||||
- **Profile**: mixerProfile
|
||||
- **Navigation**: activeWpNumber, activeWpAction, courseToHome, gpsCourseOverGround
|
||||
- **Modes** (nested): failsafe, manual, rth, poshold, althold, wp, gcs_nav, airmode, angle, horizon, cruise
|
||||
|
||||
### 2. ✅ **override.js** - Flight Control Overrides (WRITABLE)
|
||||
**Source**: `src/main/programming/logic_condition.c` (OPERATION_OVERRIDE_*)
|
||||
|
||||
Contains override operations:
|
||||
- **Throttle**: throttleScale, throttle
|
||||
- **VTX** (nested): power, band, channel
|
||||
- **Attitude** (nested): roll.angle, roll.rate, pitch.angle, pitch.rate, yaw.angle, yaw.rate
|
||||
- **Heading**: heading override
|
||||
- **RC Channels**: rcChannel[] array
|
||||
- **Arming**: armSafety
|
||||
- **OSD**: osdElement
|
||||
|
||||
### 3. ✅ **rc.js** - RC Receiver Channels (READ-ONLY)
|
||||
**Source**: RC channel handling in firmware
|
||||
|
||||
18 RC channels (rc[0] through rc[17]), each with:
|
||||
- `value`: Raw channel value (1000-2000µs)
|
||||
- `low`: Channel < 1333µs
|
||||
- `mid`: Channel 1333-1666µs
|
||||
- `high`: Channel > 1666µs
|
||||
|
||||
### 4. ✅ **gvar.js** - Global Variables (READ/WRITE)
|
||||
**Source**: `src/main/programming/global_variables.c`
|
||||
|
||||
8 global variables (gvar[0] through gvar[7]):
|
||||
- Range: -1,000,000 to 1,000,000
|
||||
- Used for storing/sharing data between logic conditions
|
||||
|
||||
### 5. ✅ **waypoint.js** - Waypoint Navigation (READ-ONLY)
|
||||
**Source**: `src/main/navigation/navigation_pos_estimator.c`
|
||||
|
||||
Waypoint mission data:
|
||||
- **Current WP**: number, action
|
||||
- **Position**: latitude, longitude, altitude
|
||||
- **Navigation**: distance, bearing
|
||||
- **Status**: missionReached, missionValid
|
||||
|
||||
### 6. ✅ **pid.js** - Programming PID Controllers
|
||||
**Source**: `src/main/programming/pid.c`
|
||||
|
||||
4 PID controllers (pid[0] through pid[3]), each with:
|
||||
- `configure()` method: setpoint, measurement, p, i, d, ff
|
||||
- `output` property: Controller output
|
||||
- `enabled` property: Controller state
|
||||
|
||||
### 7. ✅ **helpers.js** - Math & Utility Functions
|
||||
**Source**: `src/main/programming/logic_condition.c` (OPERATION_*)
|
||||
|
||||
Math functions:
|
||||
- **Basic**: min, max, abs
|
||||
- **Trig**: sin, cos, tan (degrees)
|
||||
- **Mapping**: mapInput, mapOutput
|
||||
- **Arithmetic**: add, sub, mul, div, mod (operators)
|
||||
|
||||
### 8. ✅ **events.js** - Event Handler Functions
|
||||
**Source**: Logic condition framework
|
||||
|
||||
Event handlers:
|
||||
- **on.arm**: Execute after arming with delay
|
||||
- **on.always**: Execute every cycle
|
||||
- **when**: Execute when condition true
|
||||
- **sticky**: Execute between on/off conditions
|
||||
- **edge**: Execute on rising edge
|
||||
- **delay**: Execute after condition true for duration
|
||||
- **timer**: Execute on periodic timer
|
||||
- **whenChanged**: Execute when value changes
|
||||
|
||||
### 9. ✅ **index.js** - Main Export
|
||||
Combines all definitions into single object.
|
||||
|
||||
## Implementation Status
|
||||
|
||||
| File | Status | Lines | Operands | Notes |
|
||||
|------|--------|-------|----------|-------|
|
||||
| flight.js | ✅ Ready | ~250 | FLIGHT(0-44) | All flight parameters |
|
||||
| override.js | ✅ Ready | ~150 | OPS(23-46) | All override operations |
|
||||
| rc.js | ✅ Ready | ~80 | RC(0-17) | 18 RC channels |
|
||||
| gvar.js | ✅ Ready | ~30 | GVAR(0-7) | 8 global variables |
|
||||
| waypoint.js | ✅ Ready | ~80 | WAYPOINT(0-8) | Waypoint nav data |
|
||||
| pid.js | ✅ Ready | ~70 | PID(0-3) | 4 PID controllers |
|
||||
| helpers.js | ✅ Ready | ~80 | OPS(14-39) | Math functions |
|
||||
| events.js | ✅ Ready | ~120 | - | Event handlers |
|
||||
| index.js | ✅ Ready | ~20 | - | Main export |
|
||||
|
||||
## Operand Type Mapping
|
||||
|
||||
From INAV firmware (`logic_condition.h`):
|
||||
|
||||
```c
|
||||
typedef enum logicOperandType_s {
|
||||
LOGIC_CONDITION_OPERAND_TYPE_VALUE = 0, // Literal number
|
||||
LOGIC_CONDITION_OPERAND_TYPE_RC_CHANNEL = 1, // RC channel value
|
||||
LOGIC_CONDITION_OPERAND_TYPE_FLIGHT = 2, // Flight parameter (flight.js)
|
||||
LOGIC_CONDITION_OPERAND_TYPE_FLIGHT_MODE = 3, // Flight mode
|
||||
LOGIC_CONDITION_OPERAND_TYPE_LC = 4, // Logic condition result
|
||||
LOGIC_CONDITION_OPERAND_TYPE_GVAR = 5, // Global variable (gvar.js)
|
||||
LOGIC_CONDITION_OPERAND_TYPE_PID = 6, // Programming PID (pid.js)
|
||||
LOGIC_CONDITION_OPERAND_TYPE_WAYPOINTS = 7 // Waypoint (waypoint.js)
|
||||
} logicOperandType_e;
|
||||
```
|
||||
|
||||
## Operation Mapping
|
||||
|
||||
Key operations from firmware:
|
||||
|
||||
```c
|
||||
// Conditionals
|
||||
OPERATION_TRUE = 0
|
||||
OPERATION_EQUAL = 1
|
||||
OPERATION_GREATER_THAN = 2
|
||||
OPERATION_LOWER_THAN = 3
|
||||
OPERATION_LOW = 4
|
||||
OPERATION_MID = 5
|
||||
OPERATION_HIGH = 6
|
||||
|
||||
// Logical
|
||||
OPERATION_AND = 7
|
||||
OPERATION_OR = 8
|
||||
OPERATION_NOT = 12
|
||||
OPERATION_STICKY = 13
|
||||
|
||||
// Arithmetic
|
||||
OPERATION_ADD = 14
|
||||
OPERATION_SUB = 15
|
||||
OPERATION_MUL = 16
|
||||
OPERATION_DIV = 17
|
||||
OPERATION_MOD = 18
|
||||
|
||||
// Global Variables
|
||||
OPERATION_GVAR_SET = 19
|
||||
OPERATION_INC_GVAR = 20
|
||||
OPERATION_DEC_GVAR = 21
|
||||
|
||||
// Overrides
|
||||
OPERATION_OVERRIDE_ARM_SAFETY = 23
|
||||
OPERATION_OVERRIDE_ARMING_DISABLED = 24
|
||||
OPERATION_OVERRIDE_THROTTLE_SCALE = 25
|
||||
OPERATION_OVERRIDE_THROTTLE = 26
|
||||
OPERATION_OVERRIDE_VTX_POWER = 27
|
||||
OPERATION_OVERRIDE_VTX_BAND = 28
|
||||
OPERATION_OVERRIDE_VTX_CHANNEL = 29
|
||||
|
||||
// Math
|
||||
OPERATION_MIN = 30
|
||||
OPERATION_MAX = 31
|
||||
OPERATION_ABS = 32
|
||||
OPERATION_SIN = 35
|
||||
OPERATION_COS = 36
|
||||
OPERATION_TAN = 37
|
||||
OPERATION_MAP_INPUT = 38
|
||||
OPERATION_MAP_OUTPUT = 39
|
||||
```
|
||||
|
||||
## Flight Parameter Values
|
||||
|
||||
From firmware (`logic_condition.c`):
|
||||
|
||||
```c
|
||||
LOGIC_CONDITION_OPERAND_FLIGHT_ARM_TIMER = 0
|
||||
LOGIC_CONDITION_OPERAND_FLIGHT_HOME_DISTANCE = 1
|
||||
LOGIC_CONDITION_OPERAND_FLIGHT_TRIP_DISTANCE = 2
|
||||
LOGIC_CONDITION_OPERAND_FLIGHT_RSSI = 3
|
||||
LOGIC_CONDITION_OPERAND_FLIGHT_VBAT = 4
|
||||
LOGIC_CONDITION_OPERAND_FLIGHT_CELL_VOLTAGE = 5
|
||||
LOGIC_CONDITION_OPERAND_FLIGHT_CURRENT = 6
|
||||
LOGIC_CONDITION_OPERAND_FLIGHT_MAH_DRAWN = 7
|
||||
LOGIC_CONDITION_OPERAND_FLIGHT_GPS_SATS = 9
|
||||
LOGIC_CONDITION_OPERAND_FLIGHT_GROUND_SPEED = 11
|
||||
LOGIC_CONDITION_OPERAND_FLIGHT_ALTITUDE = 12
|
||||
// ... and more
|
||||
```
|
||||
|
||||
## Usage After Implementation
|
||||
|
||||
Once these files are created:
|
||||
|
||||
### 1. Analyzer Auto-Updates
|
||||
```javascript
|
||||
// analyzer.js automatically picks up new properties
|
||||
const apiDefinitions = require('./../api/definitions/index.js');
|
||||
this.inavAPI = this.buildAPIStructure(apiDefinitions);
|
||||
// No code changes needed!
|
||||
|
||||
```
|
||||
|
||||
### 2. Decompiler Auto-Updates
|
||||
```javascript
|
||||
// decompiler.js automatically maps operands
|
||||
this.operandToProperty = this.buildOperandMapping(apiDefinitions);
|
||||
// Decompiles FLIGHT(40) → inav.flight.compassHeading automatically
|
||||
|
||||
```
|
||||
|
||||
### 3. TypeScript Auto-Generation
|
||||
```javascript
|
||||
// types.js generates Monaco definitions
|
||||
const dts = generateTypeDefinitions(apiDefinitions);
|
||||
// IntelliSense shows all properties automatically
|
||||
|
||||
```
|
||||
|
||||
### 4. Adding New Properties
|
||||
To add `flight.newSensor`:
|
||||
|
||||
1. Edit `flight.js`:
|
||||
```javascript
|
||||
newSensor: {
|
||||
type: 'number',
|
||||
desc: 'New sensor value',
|
||||
inavOperand: { type: 2, value: 50 }
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
2. Done! Everything updates automatically:
|
||||
- ✅ Analyzer validates `flight.newSensor`
|
||||
- ✅ Decompiler recognizes operand 50
|
||||
- ✅ TypeScript shows in autocomplete
|
||||
- ✅ Code generator uses correct operand
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
After creating these files:
|
||||
|
||||
- [ ] All files exist in `js/transpiler/api/definitions/`
|
||||
- [ ] `index.js` exports all definitions
|
||||
- [ ] Each property has `inavOperand` or `inavOperation`
|
||||
- [ ] Operand values match INAV firmware
|
||||
- [ ] Range values are correct
|
||||
- [ ] Readonly flags are correct
|
||||
- [ ] Nested objects are properly structured
|
||||
- [ ] `analyzer.js` imports and uses definitions
|
||||
- [ ] `decompiler.js` imports and uses definitions
|
||||
- [ ] `types.js` generates TypeScript correctly
|
||||
- [ ] Test transpilation works
|
||||
- [ ] Test decompilation works
|
||||
- [ ] Test Monaco autocomplete works
|
||||
|
||||
## References
|
||||
|
||||
- **INAV Source**: https://github.com/iNavFlight/inav
|
||||
- `src/main/programming/logic_condition.c`
|
||||
- `src/main/programming/logic_condition.h`
|
||||
- `src/main/programming/global_variables.c`
|
||||
- `src/main/programming/pid.c`
|
||||
- **Documentation**: Programming Framework.md
|
||||
- **Configurator**: programming.js, programming.html
|
||||
|
||||
## Benefits
|
||||
|
||||
✅ **Single Source of Truth**: One place to edit
|
||||
✅ **Automatic Updates**: Add property → everything works
|
||||
✅ **Type Safety**: Proper TypeScript generation
|
||||
✅ **Maintainability**: Easy to keep in sync with INAV
|
||||
✅ **Documentation**: Self-documenting API
|
||||
✅ **Validation**: Comprehensive range/type checking
|
||||
|
||||
## Maintenance
|
||||
|
||||
When adding new properties or updating existing ones, see `api_maintenance_guide.md` for the complete workflow and best practices.
|
||||
@@ -0,0 +1,409 @@
|
||||
# API Definition Maintenance Guide
|
||||
|
||||
## Single Source of Truth
|
||||
|
||||
All INAV JavaScript API definitions are centralized in:
|
||||
```
|
||||
js/transpiler/api/definitions/
|
||||
```
|
||||
|
||||
**When adding new INAV features, you only need to edit files in this directory.**
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
js/transpiler/api/definitions/
|
||||
├── index.js # Exports all definitions
|
||||
├── events.js # Event handlers (timer, whenChanged, etc.)
|
||||
├── flight.js # Flight parameters (read-only)
|
||||
├── gvar.js # Global variables (read/write)
|
||||
├── helpers.js # Math & utility functions
|
||||
├── override.js # Override settings (writable)
|
||||
├── pid.js # Programming PID controllers
|
||||
├── rc.js # RC channels (read/write)
|
||||
└── waypoint.js # Waypoint navigation
|
||||
```
|
||||
|
||||
## Definition Format
|
||||
|
||||
Each definition file exports objects following this structure:
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
propertyName: {
|
||||
type: 'number' | 'boolean' | 'string' | 'object' | 'function',
|
||||
desc: 'Human-readable description',
|
||||
unit: 'Unit of measurement (optional)',
|
||||
readonly: true | false,
|
||||
range: [min, max], // Optional: valid value range
|
||||
inavOperand: {
|
||||
type: 2, // OPERAND_TYPE constant
|
||||
value: 1 // Operand value for INAV
|
||||
},
|
||||
inavOperation: 27, // Optional: OPERATION constant
|
||||
},
|
||||
|
||||
// Nested objects
|
||||
nestedObject: {
|
||||
type: 'object',
|
||||
desc: 'Description',
|
||||
properties: {
|
||||
subProperty: {
|
||||
type: 'number',
|
||||
desc: 'Sub-property description',
|
||||
// ... same structure as above
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## What Uses These Definitions
|
||||
|
||||
### 1. **Semantic Analyzer** (`analyzer.js`)
|
||||
- Validates property access
|
||||
- Checks writable properties
|
||||
- Validates value ranges
|
||||
- **Auto-updates when definitions change**
|
||||
|
||||
### 2. **Type Definitions** (`types.js`)
|
||||
- Generates TypeScript definitions for Monaco Editor
|
||||
- Provides IntelliSense autocomplete
|
||||
- **Auto-generates from definitions**
|
||||
|
||||
### 3. **Code Generator** (`codegen.js`)
|
||||
- Maps JavaScript to INAV operands
|
||||
- Uses `inavOperand` and `inavOperation` fields
|
||||
- **Requires manual update for new operations**
|
||||
|
||||
### 4. **Decompiler** (`decompiler.js`)
|
||||
- Reverse maps INAV to JavaScript
|
||||
- Uses operand mappings from API definitions
|
||||
- **Auto-updates when definitions change**
|
||||
|
||||
## Adding a New Property
|
||||
|
||||
### Example: Adding `flight.compassHeading`
|
||||
|
||||
**1. Edit `js/transpiler/api/definitions/flight.js`:**
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
// ... existing properties ...
|
||||
|
||||
compassHeading: {
|
||||
type: 'number',
|
||||
unit: '°',
|
||||
desc: 'Compass heading in degrees (0-359)',
|
||||
readonly: true,
|
||||
range: [0, 359],
|
||||
inavOperand: {
|
||||
type: 2, // OPERAND_TYPE.FLIGHT
|
||||
value: 40 // FLIGHT_PARAM.COMPASS_HEADING
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
**2. Update `inav_constants.js` (if needed):**
|
||||
|
||||
Only if adding a completely new INAV firmware feature:
|
||||
|
||||
```javascript
|
||||
const FLIGHT_PARAM = {
|
||||
// ... existing params ...
|
||||
COMPASS_HEADING: 40
|
||||
};
|
||||
|
||||
const FLIGHT_PARAM_NAMES = {
|
||||
// ... existing names ...
|
||||
[FLIGHT_PARAM.COMPASS_HEADING]: 'compassHeading'
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
**3. That's it!**
|
||||
|
||||
The following automatically update:
|
||||
- ✅ Semantic analyzer validates `flight.compassHeading`
|
||||
- ✅ TypeScript definitions show in autocomplete
|
||||
- ✅ Range checking works automatically
|
||||
- ✅ Decompiler recognizes the property
|
||||
|
||||
## Adding a New Writable Property
|
||||
|
||||
### Example: Adding `override.vtx.frequency`
|
||||
|
||||
**1. Edit `js/transpiler/api/definitions/override.js`:**
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
// ... existing properties ...
|
||||
|
||||
vtx: {
|
||||
type: 'object',
|
||||
desc: 'VTX control',
|
||||
properties: {
|
||||
power: { /* ... */ },
|
||||
band: { /* ... */ },
|
||||
channel: { /* ... */ },
|
||||
|
||||
// NEW PROPERTY
|
||||
frequency: {
|
||||
type: 'number',
|
||||
unit: 'MHz',
|
||||
desc: 'VTX frequency in MHz',
|
||||
readonly: false, // Writable!
|
||||
range: [5000, 6000],
|
||||
inavOperation: 50 // New operation code
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
**2. Update `inav_constants.js`:**
|
||||
|
||||
```javascript
|
||||
const OPERATION = {
|
||||
// ... existing operations ...
|
||||
OVERRIDE_VTX_FREQUENCY: 50
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
**3. Update `codegen.js` (manual):**
|
||||
|
||||
Add code generation logic:
|
||||
|
||||
```javascript
|
||||
// In generateAction() method
|
||||
if (stmt.target === 'inav.override.vtx.frequency') {
|
||||
return this.pushLogicCommand(
|
||||
OPERATION.OVERRIDE_VTX_FREQUENCY,
|
||||
{ type: OPERAND_TYPE.VALUE, value: 0 },
|
||||
this.valueOperand(stmt.value),
|
||||
activatorId
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Adding a New Top-Level API Object
|
||||
|
||||
### Example: Adding `inav.sensors`
|
||||
|
||||
**1. Create `js/transpiler/api/definitions/sensors.js`:**
|
||||
|
||||
```javascript
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
acc: {
|
||||
type: 'boolean',
|
||||
desc: 'Accelerometer sensor detected',
|
||||
readonly: true,
|
||||
inavOperand: {
|
||||
type: 2, // FLIGHT
|
||||
value: 50 // New param ID
|
||||
}
|
||||
},
|
||||
|
||||
mag: {
|
||||
type: 'boolean',
|
||||
desc: 'Magnetometer sensor detected',
|
||||
readonly: true,
|
||||
inavOperand: {
|
||||
type: 2,
|
||||
value: 51
|
||||
}
|
||||
}
|
||||
|
||||
// ... more sensors
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
**2. Update `js/transpiler/api/definitions/index.js`:**
|
||||
|
||||
```javascript
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
flight: require('./inav.flight.js'),
|
||||
override: require('./inav.override.js'),
|
||||
rc: require('./rc.js'),
|
||||
gvar: require('./gvar.js'),
|
||||
waypoint: require('./inav.waypoint.js'),
|
||||
pid: require('./pid.js'),
|
||||
helpers: require('./helpers.js'),
|
||||
events: require('./events.js'),
|
||||
sensors: require('./sensors.js') // ADD THIS
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
**3. Update TypeScript types in `types.js` generation:**
|
||||
|
||||
The type generator should automatically pick it up, but verify:
|
||||
|
||||
```javascript
|
||||
// In generateTypeDefinitions()
|
||||
dts += generateInterfaceFromDefinition('sensors', apiDefinitions.sensors);
|
||||
|
||||
```
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
When adding/modifying API definitions:
|
||||
|
||||
- [ ] Property has correct `type`
|
||||
- [ ] Has descriptive `desc`
|
||||
- [ ] Has `unit` if applicable
|
||||
- [ ] `readonly` flag is correct
|
||||
- [ ] `range` is specified for numeric values
|
||||
- [ ] `inavOperand` maps to correct INAV constant
|
||||
- [ ] `inavOperation` specified for writable properties
|
||||
- [ ] Updated `index.js` if new file
|
||||
- [ ] Updated `inav_constants.js` if new INAV feature
|
||||
- [ ] Updated `codegen.js` for new writable properties
|
||||
- [ ] Tested with sample code
|
||||
- [ ] TypeScript definitions generate correctly
|
||||
|
||||
## Testing Changes
|
||||
|
||||
After modifying definitions:
|
||||
|
||||
```javascript
|
||||
// 1. Test semantic analysis
|
||||
const code = `
|
||||
if (sensors.acc) {
|
||||
// ...
|
||||
}
|
||||
`;
|
||||
|
||||
const transpiler = new Transpiler();
|
||||
const result = transpiler.transpile(code);
|
||||
// Should not have errors
|
||||
|
||||
// 2. Test type generation
|
||||
const { generateTypeDefinitions } = require('./api/types.js');
|
||||
const dts = generateTypeDefinitions(apiDefinitions);
|
||||
// Should include new properties
|
||||
|
||||
// 3. Test in Monaco Editor
|
||||
// Open configurator, verify autocomplete shows new properties
|
||||
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
### ❌ Wrong: Editing analyzer.js directly
|
||||
```javascript
|
||||
// DON'T DO THIS in analyzer.js:
|
||||
this.inavAPI = {
|
||||
'flight': {
|
||||
properties: ['homeDistance', 'newProperty'] // Hard-coded!
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### ✅ Right: Edit definition file
|
||||
```javascript
|
||||
// DO THIS in inav.flight.js:
|
||||
module.exports = {
|
||||
newProperty: {
|
||||
type: 'number',
|
||||
desc: 'New property',
|
||||
// ...
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### ❌ Wrong: Duplicating definitions
|
||||
```javascript
|
||||
// DON'T duplicate in multiple files
|
||||
// decompiler.js - NO!
|
||||
const FLIGHT_PARAMS = {
|
||||
1: 'homeDistance'
|
||||
};
|
||||
|
||||
// analyzer.js - NO!
|
||||
properties: ['homeDistance']
|
||||
|
||||
```
|
||||
|
||||
### ✅ Right: Use centralized definitions
|
||||
```javascript
|
||||
// DO THIS - import from definitions
|
||||
const apiDefinitions = require('./../api/definitions/index.js');
|
||||
const flightDef = apiDefinitions.flight;
|
||||
|
||||
```
|
||||
|
||||
## File Dependencies
|
||||
|
||||
```
|
||||
js/transpiler/api/definitions/
|
||||
├── index.js
|
||||
├── events.js
|
||||
├── flight.js
|
||||
├── gvar.js
|
||||
├── helpers.js
|
||||
├── override.js
|
||||
├── pid.js
|
||||
├── rc.js
|
||||
└── waypoint.js
|
||||
↓
|
||||
Used by:
|
||||
├── analyzer.js (validation)
|
||||
├── types.js (TypeScript generation)
|
||||
├── codegen.js (code generation)
|
||||
└── decompiler.js (via inav_constants.js)
|
||||
```
|
||||
|
||||
## Migration from Hardcoded Values
|
||||
|
||||
If you find hardcoded API definitions elsewhere in the code:
|
||||
|
||||
1. **Identify the hardcoded values**
|
||||
2. **Check if they exist in `api/definitions/`**
|
||||
3. **If not, add them to appropriate definition file**
|
||||
4. **Replace hardcoded values with imports**
|
||||
5. **Test thoroughly**
|
||||
6. **Remove old hardcoded definitions**
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
// Before (hardcoded in analyzer.js)
|
||||
this.inavAPI = {
|
||||
'flight': {
|
||||
properties: ['homeDistance', 'altitude']
|
||||
}
|
||||
};
|
||||
|
||||
// After (using definitions)
|
||||
const apiDefinitions = require('./../api/definitions/index.js');
|
||||
this.inavAPI = this.buildAPIStructure(apiDefinitions);
|
||||
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
**One Rule: Edit only `js/transpiler/api/definitions/*.js`**
|
||||
|
||||
Everything else updates automatically (except `codegen.js` which requires manual updates for new operations).
|
||||
|
||||
This ensures:
|
||||
- ✅ Single source of truth
|
||||
- ✅ No duplication
|
||||
- ✅ Easy maintenance
|
||||
- ✅ Fewer bugs
|
||||
- ✅ Automatic validation
|
||||
- ✅ Automatic type generation
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 123 KiB |
@@ -0,0 +1,217 @@
|
||||
# INAV JavaScript Transpiler - Technical Overview
|
||||
|
||||
## Overview
|
||||
|
||||
The INAV JavaScript Transpiler is a bidirectional JavaScript ↔ INAV Logic Conditions system that allows users to write flight controller logic in JavaScript instead of raw logic condition commands.
|
||||
|
||||
**System Components:**
|
||||
- **Transpiler**: JavaScript → INAV Logic Conditions
|
||||
- **Decompiler**: INAV Logic Conditions → JavaScript
|
||||
- **Semantic Analysis**: Full validation and error checking
|
||||
- **Parser**: Production-grade using Acorn
|
||||
- **Code Generation**: Optimized INAV CLI commands
|
||||
- **Integration**: Monaco Editor with IntelliSense
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ User Interface │
|
||||
│ (Monaco Editor + Event Handlers) │
|
||||
└───────────────┬───────────────────────┬─────────────────────┘
|
||||
│ │
|
||||
│ Transpile │ Load from FC
|
||||
▼ ▼
|
||||
┌───────────────────────────┐ ┌──────────────────────────────┐
|
||||
│ TRANSPILER │ │ DECOMPILER │
|
||||
│ (JavaScript → INAV) │ │ (INAV → JavaScript) │
|
||||
└───────────────────────────┘ └──────────────────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ Parser (Acorn) │ │ Analyze & Group │
|
||||
└────────┬─────────┘ └────────┬─────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ Semantic Analyzer│ │ Generate Code │
|
||||
└────────┬─────────┘ └────────┬─────────┘
|
||||
│ │
|
||||
▼ │
|
||||
┌──────────────────┐ │
|
||||
│ Optimizer │ │
|
||||
└────────┬─────────┘ │
|
||||
│ │
|
||||
▼ │
|
||||
┌──────────────────┐ │
|
||||
│ Code Generator │ │
|
||||
└────────┬─────────┘ │
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ INAV Logic Conditions │
|
||||
│ (Flight Controller MSP) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Key Features
|
||||
|
||||
### Transpiler (JavaScript → INAV)
|
||||
|
||||
✅ **Robust Parsing**
|
||||
- Uses Acorn for production-grade JavaScript parsing
|
||||
- Handles all edge cases correctly
|
||||
- Proper error messages with line/column numbers
|
||||
|
||||
✅ **Comprehensive Validation**
|
||||
- Variable scope checking
|
||||
- Property access validation
|
||||
- Range checking (gvar indices, heading values, etc.)
|
||||
- Dead code detection
|
||||
- Conflict detection
|
||||
- Uninitialized variable detection
|
||||
|
||||
✅ **Smart Code Generation**
|
||||
- Optimized logic condition output
|
||||
- Efficient operand usage
|
||||
- Proper activator chaining
|
||||
|
||||
✅ **Developer Experience**
|
||||
- Monaco Editor integration
|
||||
- Real-time syntax highlighting
|
||||
- IntelliSense autocomplete
|
||||
- Lint mode for fast feedback
|
||||
- Detailed error messages with code context
|
||||
|
||||
### Decompiler (INAV → JavaScript)
|
||||
|
||||
✅ **Intelligent Reconstruction**
|
||||
- Pattern recognition for handler types
|
||||
- Smart grouping of related conditions
|
||||
- Preserves logical structure
|
||||
|
||||
✅ **Comprehensive Coverage**
|
||||
- All INAV operations supported
|
||||
- Flight parameters
|
||||
- Global variables
|
||||
- Override operations
|
||||
- Arithmetic operations
|
||||
|
||||
✅ **Warning System**
|
||||
- Alerts about lossy conversions
|
||||
- Flags unsupported features
|
||||
- Suggests manual review where needed
|
||||
|
||||
✅ **Documentation**
|
||||
- Inline comments in generated code
|
||||
- Warning annotations
|
||||
- Original logic condition references
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Example 1: Transpilation
|
||||
|
||||
**Input JavaScript:**
|
||||
```javascript
|
||||
|
||||
if (inav.flight.homeDistance > 100) {
|
||||
inav.override.vtx.power = 3;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
**Output INAV Commands:**
|
||||
```
|
||||
logic 0 1 -1 2 2 1 0 100 0
|
||||
logic 1 1 0 27 0 0 0 3 0
|
||||
```
|
||||
|
||||
### Example 2: Decompilation
|
||||
|
||||
**Input INAV Commands:**
|
||||
```
|
||||
logic 0 1 -1 2 2 5 0 350 0
|
||||
logic 1 1 0 25 0 0 0 50 0
|
||||
```
|
||||
|
||||
**Output JavaScript:**
|
||||
```javascript
|
||||
|
||||
if (inav.flight.cellVoltage < 350) {
|
||||
inav.override.throttleScale = 50;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Example 3: Full Round-Trip
|
||||
|
||||
**Original Code:**
|
||||
```javascript
|
||||
on.arm({ delay: 1 }, () => {
|
||||
inav.gvar[0] = inav.flight.yaw;
|
||||
});
|
||||
|
||||
if (inav.flight.homeDistance > 500) {
|
||||
inav.override.vtx.power = 4;
|
||||
inav.override.throttleScale = 75;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
**Transpiled → Saved to FC → Loaded from FC:**
|
||||
```javascript
|
||||
// INAV Logic Conditions - Decompiled to JavaScript
|
||||
// Note: Comments, variable names, and some structure may be lost
|
||||
|
||||
|
||||
on.arm({ delay: 1 }, () => {
|
||||
inav.gvar[0] = inav.flight.yaw;
|
||||
});
|
||||
|
||||
if (inav.flight.homeDistance > 500) {
|
||||
inav.override.vtx.power = 4;
|
||||
inav.override.throttleScale = 75;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Known Limitations
|
||||
|
||||
### Transpiler
|
||||
|
||||
1. **Subset of JavaScript**: Only supports INAV-specific syntax
|
||||
2. **No complex expressions**: Nested function calls not supported
|
||||
3. **Limited control flow**: Only if/else supported, no loops or complex functions
|
||||
|
||||
### Decompiler
|
||||
|
||||
1. **Lossy conversion**: Comments and variable names lost
|
||||
2. **Structure changes**: Optimizations may alter original code
|
||||
3. **Complex conditions**: May not perfectly reconstruct nested logic
|
||||
4. **LC references**: References between logic conditions flagged for review
|
||||
|
||||
## Testing
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
npm test parser.test.js
|
||||
npm test analyzer.test.js
|
||||
npm test decompiler.test.js
|
||||
npm test integration.test.js
|
||||
```
|
||||
|
||||
### Test Coverage
|
||||
|
||||
- Parser: Empty input, syntax errors, edge cases
|
||||
- Analyzer: Validation, dead code, conflicts, ranges
|
||||
- Transpiler: Full pipeline, error handling
|
||||
- Decompiler: All operations, grouping, warnings
|
||||
- Integration: Monaco editor, UI events, MSP communication
|
||||
|
||||
## Further Documentation
|
||||
|
||||
- **User Guide**: See `JAVASCRIPT_PROGRAMMING_GUIDE.md` for usage patterns
|
||||
- **API Reference**: See `api_definitions_summary.md` for complete API
|
||||
- **Maintenance**: See `api_maintenance_guide.md` for adding new features
|
||||
- **Timer/WhenChanged**: See `TIMER_WHENCHANGED_EXAMPLES.md` for advanced patterns
|
||||
@@ -0,0 +1,289 @@
|
||||
# INAV JavaScript Programming Documentation
|
||||
|
||||
Complete documentation for the INAV JavaScript transpiler that allows programming flight controller logic conditions in JavaScript.
|
||||
|
||||
## 📚 Table of Contents
|
||||
|
||||
- [Quick Start](#quick-start)
|
||||
- [User Guides](#user-guides)
|
||||
- [Developer Documentation](#developer-documentation)
|
||||
- [Features](#features)
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
The INAV JavaScript transpiler converts JavaScript code into INAV logic conditions, enabling you to program flight controller behavior using familiar JavaScript syntax instead of raw logic condition commands.
|
||||
|
||||
### Features at a Glance
|
||||
|
||||
**✨ Modern Development Experience**
|
||||
|
||||

|
||||
*Real-time autocomplete with type information and documentation*
|
||||
|
||||

|
||||
*Clear error messages with line numbers and suggestions*
|
||||
|
||||
### Basic Example
|
||||
|
||||
```javascript
|
||||
|
||||
// Increase VTX power when far from home
|
||||
if (inav.flight.homeDistance > 500) {
|
||||
inav.override.vtx.power = 4;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||

|
||||
*Complete example showing VTX power control based on distance*
|
||||
|
||||
### RC Channel Override Example
|
||||
|
||||

|
||||
*Using RC channels to control behavior*
|
||||
|
||||
---
|
||||
|
||||
## User Guides
|
||||
|
||||
### 📖 [JavaScript Programming Guide](JAVASCRIPT_PROGRAMMING_GUIDE.md)
|
||||
**Start here if you're new to INAV JavaScript programming**
|
||||
|
||||
Quick reference covering:
|
||||
- Pattern guide: `if`, `edge()`, `sticky()`, `delay()`, `timer()`, `whenChanged()`
|
||||
- Common patterns (initialization, event counting, debouncing)
|
||||
- Key differences between patterns
|
||||
- Available objects and APIs
|
||||
- Debugging techniques
|
||||
|
||||
### 🔧 [Operations Reference](OPERATIONS_REFERENCE.md)
|
||||
**Complete reference for all supported operations**
|
||||
|
||||
Comprehensive guide to all INAV logic condition operations:
|
||||
- ✅ Arithmetic: `+`, `-`, `*`, `/`, `%`
|
||||
- ✅ Comparisons: `===`, `>`, `<`, `approxEqual()`
|
||||
- ✅ Logical: `&&`, `||`, `!`, `xor()`, `nand()`, `nor()`
|
||||
- ✅ Math: `Math.min()`, `Math.max()`, `Math.sin()`, `Math.cos()`, `Math.tan()`, `Math.abs()`
|
||||
- ✅ Scaling: `mapInput()`, `mapOutput()`
|
||||
- ✅ Flow control: `edge()`, `sticky()`, `delay()`, `timer()`, `whenChanged()`
|
||||
- ✅ Variables: `gvar[0-7]`, `let`, `var`
|
||||
- ✅ Overrides: `override.vtx.*`, `override.throttle`, `override.armSafety`, etc.
|
||||
- ✅ RC channel states: `rc[n].low`, `rc[n].mid`, `rc[n].high`, `rc[n].value`
|
||||
|
||||
Includes usage examples and notes for each operation.
|
||||
|
||||
### ⏱️ [Timer and WhenChanged Examples](TIMER_WHENCHANGED_EXAMPLES.md)
|
||||
**Practical examples for time-based and change-detection patterns**
|
||||
|
||||
Examples covering:
|
||||
- Timer patterns (blinking, cycling, periodic actions)
|
||||
- Change detection (RSSI monitoring, altitude tracking)
|
||||
- Combined patterns with multiple conditions
|
||||
- Real-world use cases
|
||||
|
||||
---
|
||||
|
||||
## Developer Documentation
|
||||
|
||||
### 🏗️ [Technical Implementation Overview](implementation_summary.md)
|
||||
**Architecture and design of the transpiler system**
|
||||
|
||||
Covers:
|
||||
- System architecture and component interaction
|
||||
- Transpiler pipeline (Parser → Analyzer → Optimizer → Codegen)
|
||||
- Decompiler pattern recognition
|
||||
- Key features and capabilities
|
||||
- Integration with Monaco Editor
|
||||
|
||||
### 🧪 [Testing Guide](TESTING_GUIDE.md)
|
||||
**How to test changes to the transpiler**
|
||||
|
||||
Essential reading before making changes:
|
||||
- Which components need updates (parser, analyzer, codegen, decompiler)
|
||||
- Round-trip testing template (JavaScript → CLI → JavaScript)
|
||||
- Common issues and solutions
|
||||
- Verification checklist
|
||||
- Step-by-step example of adding a new operation
|
||||
|
||||
**Always use round-trip testing when modifying the transpiler!**
|
||||
|
||||
### 🔌 [API Definitions Summary](api_definitions_summary.md)
|
||||
**Structure of the INAV API definitions**
|
||||
|
||||
Documents the API definition system:
|
||||
- Available API objects (`flight`, `override`, `rc`, `gvar`, `waypoint`, etc.)
|
||||
- Property types and metadata
|
||||
- INAV operand mappings
|
||||
- How definitions drive IntelliSense
|
||||
|
||||
### 🛠️ [API Maintenance Guide](api_maintenance_guide.md)
|
||||
**How to add, modify, or maintain API definitions**
|
||||
|
||||
Step-by-step instructions for:
|
||||
- Adding new properties to existing APIs
|
||||
- Creating new API objects
|
||||
- Updating operand mappings
|
||||
- Testing API changes
|
||||
- Common pitfalls
|
||||
|
||||
### ⏲️ [Timer and WhenChanged Implementation](TIMER_WHENCHANGED_IMPLEMENTATION.md)
|
||||
**Technical details of TIMER and DELTA operations**
|
||||
|
||||
Implementation guide for:
|
||||
- How TIMER operation works (operations 49)
|
||||
- How DELTA operation works (operation 50)
|
||||
- AST representation
|
||||
- Code generation strategy
|
||||
- Decompiler pattern recognition
|
||||
|
||||
### ⚙️ [Generate Constants README](GENERATE_CONSTANTS_README.md)
|
||||
**How to regenerate INAV constants from firmware**
|
||||
|
||||
Instructions for:
|
||||
- Extracting operation codes from firmware
|
||||
- Regenerating `inav_constants.js`
|
||||
- Keeping constants in sync with firmware
|
||||
- What to do when firmware adds new operations
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
### Bidirectional Translation
|
||||
|
||||
- **Transpiler**: JavaScript → INAV Logic Conditions
|
||||
- **Decompiler**: INAV Logic Conditions → JavaScript
|
||||
- **Round-trip capable**: Code survives transpile/decompile cycles
|
||||
|
||||
### Development Experience
|
||||
|
||||
- **Monaco Editor Integration**: Full-featured code editor
|
||||
- **IntelliSense**: Context-aware autocomplete with documentation
|
||||
- **Real-time Validation**: Immediate feedback on syntax errors
|
||||
- **Syntax Highlighting**: JavaScript syntax with INAV-specific extensions
|
||||
- **Error Messages**: Clear, actionable error messages with line numbers
|
||||
|
||||
### Language Support
|
||||
|
||||
**All INAV Operations Supported:**
|
||||
- Arithmetic operations
|
||||
- Comparison and logical operations
|
||||
- Math functions (trigonometry, min/max)
|
||||
- Flow control (edge detection, sticky conditions, delays, timers)
|
||||
- Variable management (global variables, let/var)
|
||||
- RC channel access and state detection
|
||||
- Flight parameter overrides
|
||||
- Waypoint navigation
|
||||
|
||||
**JavaScript Features:**
|
||||
- Namespaced API access: `inav.flight.*`, `inav.override.*`, `inav.events.*`
|
||||
- `let`/`const` variables: compile-time constant substitution
|
||||
- `var` variables: allocated to global variables
|
||||
- Ternary operator: `condition ? value1 : value2`
|
||||
- Arrow functions: `() => condition`
|
||||
- Object property access: `inav.flight.altitude`, `inav.rc[0].value`
|
||||
- Binary expressions: `+`, `-`, `*`, `/`, `%`
|
||||
- Comparison operators: `>`, `<`, `===`
|
||||
- Logical operators: `&&`, `||`, `!`
|
||||
- Math methods: `Math.min()`, `Math.max()`, `Math.sin()`, etc.
|
||||
- Flight mode detection: `inav.flight.mode.poshold`, `inav.flight.mode.rth`, etc.
|
||||
- PID controller outputs: `inav.pid[0-3].output`
|
||||
|
||||
### Validation
|
||||
|
||||
- Property access validation against API definitions
|
||||
- Range checking (gvar indices, heading values, etc.)
|
||||
- Type checking for function arguments
|
||||
- Dead code detection
|
||||
- Uninitialized variable detection
|
||||
- Helpful suggestions for common mistakes
|
||||
|
||||
### Optimization
|
||||
|
||||
- Common Subexpression Elimination (CSE)
|
||||
- Efficient operand usage
|
||||
- Optimized GVAR_INC/GVAR_DEC generation
|
||||
- Minimal logic condition count
|
||||
|
||||
---
|
||||
|
||||
## File Organization
|
||||
|
||||
```
|
||||
js/transpiler/
|
||||
├── docs/ # Documentation (this directory)
|
||||
│ ├── index.md # This file - documentation index
|
||||
│ ├── JAVASCRIPT_PROGRAMMING_GUIDE.md # User guide
|
||||
│ ├── OPERATIONS_REFERENCE.md # All operations
|
||||
│ ├── TIMER_WHENCHANGED_EXAMPLES.md # Timer/change examples
|
||||
│ ├── implementation_summary.md # Technical overview
|
||||
│ ├── TESTING_GUIDE.md # Testing workflow
|
||||
│ ├── api_definitions_summary.md # API structure
|
||||
│ ├── api_maintenance_guide.md # API maintenance
|
||||
│ ├── GENERATE_CONSTANTS_README.md # Constants generation
|
||||
│ ├── TIMER_WHENCHANGED_IMPLEMENTATION.md # Implementation details
|
||||
│ ├── intellisense.png # Screenshot: autocomplete
|
||||
│ ├── warnings.png # Screenshot: errors
|
||||
│ ├── example_vtx_power.png # Screenshot: VTX example
|
||||
│ └── example_override_rc.png # Screenshot: RC example
|
||||
├── api/ # API definitions
|
||||
│ └── definitions/ # INAV API object definitions
|
||||
├── editor/ # Monaco editor integration
|
||||
│ ├── monaco_setup.js # Editor configuration
|
||||
│ ├── intellisense.js # Autocomplete provider
|
||||
│ └── diagnostics.js # Real-time validation
|
||||
├── transpiler/ # Core transpiler
|
||||
│ ├── index.js # Main transpiler entry point
|
||||
│ ├── parser.js # JavaScript parser (Acorn wrapper)
|
||||
│ ├── analyzer.js # Semantic analysis
|
||||
│ ├── optimizer.js # Code optimization (CSE)
|
||||
│ ├── codegen.js # INAV command generation
|
||||
│ ├── decompiler.js # INAV → JavaScript
|
||||
│ ├── inav_constants.js # Operation codes and types
|
||||
│ ├── variable_handler.js # let/var variable management
|
||||
│ ├── arrow_function_helper.js # Arrow function utilities
|
||||
│ └── error_handler.js # Error collection
|
||||
└── tools/ # Build tools
|
||||
└── generate-constants.js # Extract constants from firmware
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Getting Help
|
||||
|
||||
### Common Questions
|
||||
|
||||
**Q: Which pattern should I use?**
|
||||
- Use `if` for continuous control (checking every cycle)
|
||||
- Use `edge()` for one-time actions when condition becomes true
|
||||
- Use `sticky()` for latching conditions with hysteresis
|
||||
- Use `delay()` for actions that need sustained conditions
|
||||
- Use `timer()` for periodic toggling
|
||||
- Use `whenChanged()` for detecting value changes
|
||||
|
||||
See the [JavaScript Programming Guide](JAVASCRIPT_PROGRAMMING_GUIDE.md) for detailed examples.
|
||||
|
||||
**Q: How do I debug my code?**
|
||||
Use global variables to track state and view them in the OSD or configurator. See the debugging section in the [JavaScript Programming Guide](JAVASCRIPT_PROGRAMMING_GUIDE.md).
|
||||
|
||||
**Q: What operations are supported?**
|
||||
All INAV logic condition operations! See [Operations Reference](OPERATIONS_REFERENCE.md) for the complete list.
|
||||
|
||||
**Q: How do I contribute?**
|
||||
Read the [Testing Guide](TESTING_GUIDE.md) to understand the testing workflow, then make your changes ensuring all 4 components (parser, analyzer, codegen, decompiler) are updated and round-trip tested.
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
**2025-11-25**: Complete implementation
|
||||
- All INAV logic condition operations now supported
|
||||
- RC channel state detection (LOW/MID/HIGH)
|
||||
- XOR/NAND/NOR logical operations
|
||||
- APPROX_EQUAL comparison
|
||||
- MAP_INPUT/MAP_OUTPUT scaling
|
||||
- Comprehensive documentation
|
||||
|
||||
**Last Updated**: 2025-11-25
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
Reference in New Issue
Block a user