Cosmetics
[opentx.git] / radio / src / lua / api_model.cpp
blob990d477d4641afec437dfd106e83ed115f2e2983
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include <ctype.h>
22 #include <stdio.h>
23 #include "opentx.h"
24 #include "lua_api.h"
25 #include "timers.h"
27 /*luadoc
28 @function model.getInfo()
30 Get current Model information
32 @retval table model information:
33 * `name` (string) model name
34 * `bitmap` (string) bitmap name (not present on X7)
36 @status current Introduced in 2.0.6, changed in 2.2.0
38 static int luaModelGetInfo(lua_State *L)
40 lua_newtable(L);
41 lua_pushtablezstring(L, "name", g_model.header.name);
42 #if LCD_DEPTH > 1
43 lua_pushtablenzstring(L, "bitmap", g_model.header.bitmap);
44 #endif
45 return 1;
48 /*luadoc
49 @function model.setInfo(value)
51 Set the current Model information
53 @param value model information data, see model.getInfo()
55 @notice If a parameter is missing from the value, then
56 that parameter remains unchanged.
58 @status current Introduced in 2.0.6, changed in TODO
60 static int luaModelSetInfo(lua_State *L)
62 luaL_checktype(L, -1, LUA_TTABLE);
63 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
64 luaL_checktype(L, -2, LUA_TSTRING); // key is string
65 const char * key = luaL_checkstring(L, -2);
66 if (!strcmp(key, "name")) {
67 const char * name = luaL_checkstring(L, -1);
68 str2zchar(g_model.header.name, name, sizeof(g_model.header.name));
69 #if defined(EEPROM)
70 memcpy(modelHeaders[g_eeGeneral.currModel].name, g_model.header.name, sizeof(g_model.header.name));
71 #endif
73 #if LCD_DEPTH > 1
74 else if (!strcmp(key, "bitmap")) {
75 const char * name = luaL_checkstring(L, -1);
76 strncpy(g_model.header.bitmap, name, sizeof(g_model.header.bitmap));
78 #endif
80 storageDirty(EE_MODEL);
81 return 0;
84 /*luadoc
85 @function model.getModule(index)
87 Get RF module parameters
89 `subType` values:
90 * -1 OFF
91 * 0 D16
92 * 1 D8
93 * 2 LR12
95 @param index (number) module index (0 for internal, 1 for external)
97 @retval nil requested module does not exist
99 @retval table module parameters:
100 * `subType` (number) protocol index
101 * `modelId` (number) receiver number
102 * `firstChannel` (number) start channel (0 is CH1)
103 * `channelsCount` (number) number of channels sent to module
105 @status current Introduced in TODO
107 static int luaModelGetModule(lua_State *L)
109 unsigned int idx = luaL_checkunsigned(L, 1);
110 if (idx < NUM_MODULES) {
111 ModuleData & module = g_model.moduleData[idx];
112 lua_newtable(L);
113 lua_pushtableinteger(L, "subType", module.subType);
114 lua_pushtableinteger(L, "modelId", g_model.header.modelId[idx]);
115 lua_pushtableinteger(L, "firstChannel", module.channelsStart);
116 lua_pushtableinteger(L, "channelsCount", module.channelsCount + 8);
118 else {
119 lua_pushnil(L);
121 return 1;
124 /*luadoc
125 @function model.setModule(index, value)
127 Set RF module parameters
129 @param index (number) module index (0 for internal, 1 for external)
131 @param value module parameters, see model.getModule()
133 @notice If a parameter is missing from the value, then
134 that parameter remains unchanged.
136 @status current Introduced in TODO
138 static int luaModelSetModule(lua_State *L)
140 unsigned int idx = luaL_checkunsigned(L, 1);
142 if (idx < NUM_MODULES) {
143 ModuleData & module = g_model.moduleData[idx];
144 luaL_checktype(L, -1, LUA_TTABLE);
145 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
146 luaL_checktype(L, -2, LUA_TSTRING); // key is string
147 const char * key = luaL_checkstring(L, -2);
148 if (!strcmp(key, "subType")) {
149 module.subType = luaL_checkinteger(L, -1);
151 else if (!strcmp(key, "modelId")) {
152 g_model.header.modelId[idx] = luaL_checkinteger(L, -1);
153 #if defined(EEPROM)
154 modelHeaders[g_eeGeneral.currModel].modelId[idx] = g_model.header.modelId[idx];
155 #endif
157 else if (!strcmp(key, "firstChannel")) {
158 module.channelsStart = luaL_checkinteger(L, -1);
160 else if (!strcmp(key, "channelsCount")) {
161 module.channelsCount = luaL_checkinteger(L, -1) - 8;
164 storageDirty(EE_MODEL);
166 return 0;
169 /*luadoc
170 @function model.getTimer(timer)
172 Get model timer parameters
174 @param timer (number) timer index (0 for Timer 1)
176 @retval nil requested timer does not exist
178 @retval table timer parameters:
179 * `mode` (number) timer trigger source: off, abs, stk, stk%, sw/!sw, !m_sw/!m_sw
180 * `start` (number) start value [seconds], 0 for up timer, 0> down timer
181 * `value` (number) current value [seconds]
182 * `countdownBeep` (number) countdown beep (0­ = silent, 1 =­ beeps, 2­ = voice)
183 * `minuteBeep` (boolean) minute beep
184 * `persistent` (number) persistent timer
186 @status current Introduced in 2.0.0
188 static int luaModelGetTimer(lua_State *L)
190 unsigned int idx = luaL_checkunsigned(L, 1);
191 if (idx < MAX_TIMERS) {
192 TimerData & timer = g_model.timers[idx];
193 lua_newtable(L);
194 lua_pushtableinteger(L, "mode", timer.mode);
195 lua_pushtableinteger(L, "start", timer.start);
196 lua_pushtableinteger(L, "value", timersStates[idx].val);
197 lua_pushtableinteger(L, "countdownBeep", timer.countdownBeep);
198 lua_pushtableboolean(L, "minuteBeep", timer.minuteBeep);
199 lua_pushtableinteger(L, "persistent", timer.persistent);
201 else {
202 lua_pushnil(L);
204 return 1;
207 /*luadoc
208 @function model.setTimer(timer, value)
210 Set model timer parameters
212 @param timer (number) timer index (0 for Timer 1)
214 @param value timer parameters, see model.getTimer()
216 @notice If a parameter is missing from the value, then
217 that parameter remains unchanged.
219 @status current Introduced in 2.0.0
221 static int luaModelSetTimer(lua_State *L)
223 unsigned int idx = luaL_checkunsigned(L, 1);
225 if (idx < MAX_TIMERS) {
226 TimerData & timer = g_model.timers[idx];
227 luaL_checktype(L, -1, LUA_TTABLE);
228 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
229 luaL_checktype(L, -2, LUA_TSTRING); // key is string
230 const char * key = luaL_checkstring(L, -2);
231 if (!strcmp(key, "mode")) {
232 timer.mode = luaL_checkinteger(L, -1);
234 else if (!strcmp(key, "start")) {
235 timer.start = luaL_checkinteger(L, -1);
237 else if (!strcmp(key, "value")) {
238 timersStates[idx].val = luaL_checkinteger(L, -1);
240 else if (!strcmp(key, "countdownBeep")) {
241 timer.countdownBeep = luaL_checkinteger(L, -1);
243 else if (!strcmp(key, "minuteBeep")) {
244 timer.minuteBeep = lua_toboolean(L, -1);
246 else if (!strcmp(key, "persistent")) {
247 timer.persistent = luaL_checkinteger(L, -1);
250 storageDirty(EE_MODEL);
252 return 0;
255 /*luadoc
256 @function model.resetTimer(timer)
258 Reset model timer to a startup value
260 @param timer (number) timer index (0 for Timer 1)
262 @status current Introduced in TODO
264 static int luaModelResetTimer(lua_State *L)
266 unsigned int idx = luaL_checkunsigned(L, 1);
267 if (idx < MAX_TIMERS) {
268 timerReset(idx);
270 return 0;
273 static unsigned int getFirstInput(unsigned int chn)
275 for (unsigned int i=0; i<MAX_EXPOS; i++) {
276 ExpoData * expo = expoAddress(i);
277 if (!expo->srcRaw || expo->chn >= chn) {
278 return i;
281 return 0;
284 static unsigned int getInputsCountFromFirst(unsigned int chn, unsigned int first)
286 unsigned int count = 0;
287 for (unsigned int i=first; i<MAX_EXPOS; i++) {
288 ExpoData * expo = expoAddress(i);
289 if (!expo->srcRaw || expo->chn!=chn) break;
290 count++;
292 return count;
295 static unsigned int getInputsCount(unsigned int chn)
297 return getInputsCountFromFirst(chn, getFirstInput(chn));
300 /*luadoc
301 @function model.getInputsCount(input)
303 Return number of lines for given input
305 @param input (unsigned number) input number (use 0 for Input1)
307 @retval number number of configured lines for given input
309 @status current Introduced in 2.0.0
311 static int luaModelGetInputsCount(lua_State *L)
313 unsigned int chn = luaL_checkunsigned(L, 1);
314 int count = getInputsCount(chn);
315 lua_pushinteger(L, count);
316 return 1;
319 /*luadoc
320 @function model.getInput(input, line)
322 Return input data for given input and line number
324 @param input (unsigned number) input number (use 0 for Input1)
326 @param line (unsigned number) input line (use 0 for first line)
328 @retval nil requested input or line does not exist
330 @retval table input data:
331 * `name` (string) input line name
332 * `source` (number) input source index
333 * `weight` (number) input weight
334 * `offset` (number) input offset
335 * `switch` (number) input switch index
336 * `curveType` (number) curve type (function, expo, custom curve)
337 * `curveValue` (number) curve index
338 * `carryTrim` (boolean) input trims applied
340 @status current Introduced in 2.0.0, curveType/curveValue/carryTrim added in 2.3
342 static int luaModelGetInput(lua_State *L)
344 unsigned int chn = luaL_checkunsigned(L, 1);
345 unsigned int idx = luaL_checkunsigned(L, 2);
346 unsigned int first = getFirstInput(chn);
347 unsigned int count = getInputsCountFromFirst(chn, first);
348 if (idx < count) {
349 ExpoData * expo = expoAddress(first+idx);
350 lua_newtable(L);
351 lua_pushtablezstring(L, "name", expo->name);
352 lua_pushtableinteger(L, "source", expo->srcRaw);
353 lua_pushtableinteger(L, "weight", expo->weight);
354 lua_pushtableinteger(L, "offset", expo->offset);
355 lua_pushtableinteger(L, "switch", expo->swtch);
356 lua_pushtableinteger(L, "curveType", expo->curve.type);
357 lua_pushtableinteger(L, "curveValue", expo->curve.value);
358 lua_pushtableinteger(L, "carryTrim", expo->carryTrim);
360 else {
361 lua_pushnil(L);
363 return 1;
366 /*luadoc
367 @function model.insertInput(input, line, value)
369 Insert an Input at specified line
371 @param input (unsigned number) input number (use 0 for Input1)
373 @param line (unsigned number) input line (use 0 for first line)
375 @param value (table) input data, see model.getInput()
377 @status current Introduced in 2.0.0, curveType/curveValue/carryTrim added in 2.3
379 static int luaModelInsertInput(lua_State *L)
381 unsigned int chn = luaL_checkunsigned(L, 1);
382 unsigned int idx = luaL_checkunsigned(L, 2);
384 unsigned int first = getFirstInput(chn);
385 unsigned int count = getInputsCountFromFirst(chn, first);
387 if (chn<MAX_INPUTS && getExposCount()<MAX_EXPOS && idx<=count) {
388 idx = first + idx;
389 s_currCh = chn + 1;
390 insertExpo(idx);
391 ExpoData * expo = expoAddress(idx);
392 luaL_checktype(L, -1, LUA_TTABLE);
393 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
394 luaL_checktype(L, -2, LUA_TSTRING); // key is string
395 const char * key = luaL_checkstring(L, -2);
396 if (!strcmp(key, "name")) {
397 const char * name = luaL_checkstring(L, -1);
398 str2zchar(expo->name, name, sizeof(expo->name));
400 else if (!strcmp(key, "source")) {
401 expo->srcRaw = luaL_checkinteger(L, -1);
403 else if (!strcmp(key, "weight")) {
404 expo->weight = luaL_checkinteger(L, -1);
406 else if (!strcmp(key, "offset")) {
407 expo->offset = luaL_checkinteger(L, -1);
409 else if (!strcmp(key, "switch")) {
410 expo->swtch = luaL_checkinteger(L, -1);
412 else if (!strcmp(key, "curveType")) {
413 expo->curve.type = luaL_checkinteger(L, -1);
415 else if (!strcmp(key, "curveValue")) {
416 expo->curve.value = luaL_checkinteger(L, -1);
418 else if (!strcmp(key, "carryTrim")) {
419 expo->carryTrim = lua_toboolean(L, -1);
424 return 0;
427 /*luadoc
428 @function model.deleteInput(input, line)
430 Delete line from specified input
432 @param input (unsigned number) input number (use 0 for Input1)
434 @param line (unsigned number) input line (use 0 for first line)
436 @status current Introduced in 2.0.0
438 static int luaModelDeleteInput(lua_State *L)
440 unsigned int chn = luaL_checkunsigned(L, 1);
441 unsigned int idx = luaL_checkunsigned(L, 2);
443 int first = getFirstInput(chn);
444 unsigned int count = getInputsCountFromFirst(chn, first);
446 if (idx < count) {
447 deleteExpo(first+idx);
450 return 0;
453 /*luadoc
454 @function model.deleteInputs()
456 Delete all Inputs
458 @status current Introduced in 2.0.0
460 static int luaModelDeleteInputs(lua_State *L)
462 clearInputs();
463 return 0;
466 /*luadoc
467 @function model.defaultInputs()
469 Set all inputs to defaults
471 @status current Introduced in 2.0.0
473 static int luaModelDefaultInputs(lua_State *L)
475 defaultInputs();
476 return 0;
479 static unsigned int getFirstMix(unsigned int chn)
481 for (unsigned int i=0; i<MAX_MIXERS; i++) {
482 MixData * mix = mixAddress(i);
483 if (!mix->srcRaw || mix->destCh>=chn) {
484 return i;
487 return 0;
490 static unsigned int getMixesCountFromFirst(unsigned int chn, unsigned int first)
492 unsigned int count = 0;
493 for (unsigned int i=first; i<MAX_MIXERS; i++) {
494 MixData * mix = mixAddress(i);
495 if (!mix->srcRaw || mix->destCh!=chn) break;
496 count++;
498 return count;
501 static unsigned int getMixesCount(unsigned int chn)
503 return getMixesCountFromFirst(chn, getFirstMix(chn));
506 /*luadoc
507 @function model.getMixesCount(channel)
509 Get the number of Mixer lines that the specified Channel has
511 @param channel (unsigned number) channel number (use 0 for CH1)
513 @retval number number of mixes for requested channel
515 @status current Introduced in 2.0.0
517 static int luaModelGetMixesCount(lua_State *L)
519 unsigned int chn = luaL_checkunsigned(L, 1);
520 unsigned int count = getMixesCount(chn);
521 lua_pushinteger(L, count);
522 return 1;
525 /*luadoc
526 @function model.getMix(channel, line)
528 Get configuration for specified Mix
530 @param channel (unsigned number) channel number (use 0 for CH1)
532 @param line (unsigned number) mix number (use 0 for first line(mix))
534 @retval nil requested channel or line does not exist
536 @retval table mix data:
537 * `name` (string) mix line name
538 * `source` (number) source index
539 * `weight` (number) weight (1024 == 100%) value or GVAR1..9 = 4096..4011, -GVAR1..9 = 4095..4087
540 * `offset` (number) offset value or GVAR1..9 = 4096..4011, -GVAR1..9 = 4095..4087
541 * `switch` (number) switch index
542 * `multiplex` (number) multiplex (0 = ADD, 1 = MULTIPLY, 2 = REPLACE)
543 * `curveType` (number) curve type (function, expo, custom curve)
544 * `curveValue` (number) curve index
545 * `flightModes` (number) bit-mask of active flight modes
546 * `carryTrim` (boolean) carry trim
547 * `mixWarn` (number) warning (0 = off, 1 = 1 beep, .. 3 = 3 beeps)
548 * `delayUp` (number) delay up (time in 1/10 s)
549 * `delayDown` (number) delay down
550 * `speedUp` (number) speed up
551 * `speedDown` (number) speed down
553 @status current Introduced in 2.0.0, parameters below `multiplex` added in 2.0.13
555 static int luaModelGetMix(lua_State *L)
557 unsigned int chn = luaL_checkunsigned(L, 1);
558 unsigned int idx = luaL_checkunsigned(L, 2);
559 unsigned int first = getFirstMix(chn);
560 unsigned int count = getMixesCountFromFirst(chn, first);
561 if (idx < count) {
562 MixData * mix = mixAddress(first+idx);
563 lua_newtable(L);
564 lua_pushtablezstring(L, "name", mix->name);
565 lua_pushtableinteger(L, "source", mix->srcRaw);
566 lua_pushtableinteger(L, "weight", mix->weight);
567 lua_pushtableinteger(L, "offset", mix->offset);
568 lua_pushtableinteger(L, "switch", mix->swtch);
569 lua_pushtableinteger(L, "curveType", mix->curve.type);
570 lua_pushtableinteger(L, "curveValue", mix->curve.value);
571 lua_pushtableinteger(L, "multiplex", mix->mltpx);
572 lua_pushtableinteger(L, "flightModes", mix->flightModes);
573 lua_pushtableboolean(L, "carryTrim", mix->carryTrim);
574 lua_pushtableinteger(L, "mixWarn", mix->mixWarn);
575 lua_pushtableinteger(L, "delayUp", mix->delayUp);
576 lua_pushtableinteger(L, "delayDown", mix->delayDown);
577 lua_pushtableinteger(L, "speedUp", mix->speedUp);
578 lua_pushtableinteger(L, "speedDown", mix->speedDown);
580 else {
581 lua_pushnil(L);
583 return 1;
586 /*luadoc
587 @function model.insertMix(channel, line, value)
589 Insert a mixer line into Channel
591 @param channel (unsigned number) channel number (use 0 for CH1)
593 @param line (unsigned number) mix number (use 0 for first line(mix))
595 @param value (table) see model.getMix() for table format
597 @status current Introduced in 2.0.0, parameters below `multiplex` added in 2.0.13
599 static int luaModelInsertMix(lua_State *L)
601 unsigned int chn = luaL_checkunsigned(L, 1);
602 unsigned int idx = luaL_checkunsigned(L, 2);
604 unsigned int first = getFirstMix(chn);
605 unsigned int count = getMixesCountFromFirst(chn, first);
607 if (chn<MAX_OUTPUT_CHANNELS && getMixesCount()<MAX_MIXERS && idx<=count) {
608 idx += first;
609 s_currCh = chn+1;
610 insertMix(idx);
611 MixData *mix = mixAddress(idx);
612 luaL_checktype(L, -1, LUA_TTABLE);
613 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
614 luaL_checktype(L, -2, LUA_TSTRING); // key is string
615 const char * key = luaL_checkstring(L, -2);
616 if (!strcmp(key, "name")) {
617 const char * name = luaL_checkstring(L, -1);
618 str2zchar(mix->name, name, sizeof(mix->name));
620 else if (!strcmp(key, "source")) {
621 mix->srcRaw = luaL_checkinteger(L, -1);
623 else if (!strcmp(key, "weight")) {
624 mix->weight = luaL_checkinteger(L, -1);
626 else if (!strcmp(key, "offset")) {
627 mix->offset = luaL_checkinteger(L, -1);
629 else if (!strcmp(key, "switch")) {
630 mix->swtch = luaL_checkinteger(L, -1);
632 else if (!strcmp(key, "curveType")) {
633 mix->curve.type = luaL_checkinteger(L, -1);
635 else if (!strcmp(key, "curveValue")) {
636 mix->curve.value = luaL_checkinteger(L, -1);
638 else if (!strcmp(key, "multiplex")) {
639 mix->mltpx = luaL_checkinteger(L, -1);
641 else if (!strcmp(key, "flightModes")) {
642 mix->flightModes = luaL_checkinteger(L, -1);
644 else if (!strcmp(key, "carryTrim")) {
645 mix->carryTrim = lua_toboolean(L, -1);
647 else if (!strcmp(key, "mixWarn")) {
648 mix->mixWarn = luaL_checkinteger(L, -1);
650 else if (!strcmp(key, "delayUp")) {
651 mix->delayUp = luaL_checkinteger(L, -1);
653 else if (!strcmp(key, "delayDown")) {
654 mix->delayDown = luaL_checkinteger(L, -1);
656 else if (!strcmp(key, "speedUp")) {
657 mix->speedUp = luaL_checkinteger(L, -1);
659 else if (!strcmp(key, "speedDown")) {
660 mix->speedDown = luaL_checkinteger(L, -1);
665 return 0;
668 /*luadoc
669 @function model.deleteMix(channel, line)
671 Delete mixer line from specified Channel
673 @param channel (unsigned number) channel number (use 0 for CH1)
675 @param line (unsigned number) mix number (use 0 for first line(mix))
677 @status current Introduced in 2.0.0
679 static int luaModelDeleteMix(lua_State *L)
681 unsigned int chn = luaL_checkunsigned(L, 1);
682 unsigned int idx = luaL_checkunsigned(L, 2);
684 unsigned int first = getFirstMix(chn);
685 unsigned int count = getMixesCountFromFirst(chn, first);
687 if (idx < count) {
688 deleteMix(first+idx);
691 return 0;
694 /*luadoc
695 @function model.deleteMixes()
697 Remove all mixers
699 @status current Introduced in 2.0.0
701 static int luaModelDeleteMixes(lua_State *L)
703 memset(g_model.mixData, 0, sizeof(g_model.mixData));
704 return 0;
707 /*luadoc
708 @function model.getLogicalSwitch(switch)
710 Get Logical Switch parameters
712 @param switch (unsigned number) logical switch number (use 0 for LS1)
714 @retval nil requested logical switch does not exist
716 @retval table logical switch data:
717 * `func` (number) function index
718 * `v1` (number) V1 value (index)
719 * `v2` (number) V2 value (index or value)
720 * `v3` (number) V3 value (index or value)
721 * `and` (number) AND switch index
722 * `delay` (number) delay (time in 1/10 s)
723 * `duration` (number) duration (time in 1/10 s)
725 @status current Introduced in 2.0.0
727 static int luaModelGetLogicalSwitch(lua_State *L)
729 unsigned int idx = luaL_checkunsigned(L, 1);
730 if (idx < MAX_LOGICAL_SWITCHES) {
731 LogicalSwitchData * sw = lswAddress(idx);
732 lua_newtable(L);
733 lua_pushtableinteger(L, "func", sw->func);
734 lua_pushtableinteger(L, "v1", sw->v1);
735 lua_pushtableinteger(L, "v2", sw->v2);
736 lua_pushtableinteger(L, "v3", sw->v3);
737 lua_pushtableinteger(L, "and", sw->andsw);
738 lua_pushtableinteger(L, "delay", sw->delay);
739 lua_pushtableinteger(L, "duration", sw->duration);
741 else {
742 lua_pushnil(L);
744 return 1;
747 /*luadoc
748 @function model.setLogicalSwitch(switch, value)
750 Set Logical Switch parameters
752 @param switch (unsigned number) logical switch number (use 0 for LS1)
754 @param value (table) see model.getLogicalSwitch() for table format
756 @notice If a parameter is missing from the value, then
757 that parameter remains unchanged.
759 @notice To set the `and` member (which is Lua keyword)
760 use the following syntax: `model.setLogicalSwitch(30, {func=4,v1=1,v2=-99, ["and"]=24})`
762 @status current Introduced in 2.0.0
764 static int luaModelSetLogicalSwitch(lua_State *L)
766 unsigned int idx = luaL_checkunsigned(L, 1);
767 if (idx < MAX_LOGICAL_SWITCHES) {
768 LogicalSwitchData * sw = lswAddress(idx);
769 memclear(sw, sizeof(LogicalSwitchData));
770 luaL_checktype(L, -1, LUA_TTABLE);
771 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
772 luaL_checktype(L, -2, LUA_TSTRING); // key is string
773 const char * key = luaL_checkstring(L, -2);
774 if (!strcmp(key, "func")) {
775 sw->func = luaL_checkinteger(L, -1);
777 else if (!strcmp(key, "v1")) {
778 sw->v1 = luaL_checkinteger(L, -1);
780 else if (!strcmp(key, "v2")) {
781 sw->v2 = luaL_checkinteger(L, -1);
783 else if (!strcmp(key, "v3")) {
784 sw->v3 = luaL_checkinteger(L, -1);
786 else if (!strcmp(key, "and")) {
787 sw->andsw = luaL_checkinteger(L, -1);
789 else if (!strcmp(key, "delay")) {
790 sw->delay = luaL_checkinteger(L, -1);
792 else if (!strcmp(key, "duration")) {
793 sw->duration = luaL_checkinteger(L, -1);
796 storageDirty(EE_MODEL);
799 return 0;
802 /*luadoc
803 @function model.getCurve(curve)
805 Get Curve parameters
807 @param curve (unsigned number) curve number (use 0 for Curve1)
809 @retval nil requested curve does not exist
811 @retval table curve data:
812 * `name` (string) name
813 * `type` (number) type
814 * `smooth` (boolean) smooth
815 * `points` (number) number of points
816 * `y` (table) table of Y values:
817 * `key` is point number (zero based)
818 * `value` is y value
819 * `x` (table) **only included for custom curve type**:
820 * `key` is point number (zero based)
821 * `value` is x value
823 Note that functions returns the tables starting with index 0 contrary to LUA's
824 usual index starting with 1
826 @status current Introduced in 2.0.12
828 static int luaModelGetCurve(lua_State *L)
830 unsigned int idx = luaL_checkunsigned(L, 1);
831 if (idx < MAX_CURVES) {
832 CurveData & curveData = g_model.curves[idx];
833 lua_newtable(L);
834 lua_pushtablezstring(L, "name", curveData.name);
835 lua_pushtableinteger(L, "type", curveData.type);
836 lua_pushtableboolean(L, "smooth", curveData.smooth);
837 lua_pushtableinteger(L, "points", curveData.points + 5);
838 lua_pushstring(L, "y");
839 lua_newtable(L);
840 int8_t * point = curveAddress(idx);
841 for (int i=0; i < curveData.points + 5; i++) {
842 lua_pushinteger(L, i);
843 lua_pushinteger(L, *point++);
844 lua_settable(L, -3);
846 lua_settable(L, -3);
847 if (curveData.type == CURVE_TYPE_CUSTOM) {
848 lua_pushstring(L, "x");
849 lua_newtable(L);
850 lua_pushinteger(L, 0);
851 lua_pushinteger(L, -100);
852 lua_settable(L, -3);
853 for (int i=0; i < curveData.points + 3; i++) {
854 lua_pushinteger(L, i+1);
855 lua_pushinteger(L, *point++);
856 lua_settable(L, -3);
858 lua_pushinteger(L, curveData.points + 4);
859 lua_pushinteger(L, 100);
860 lua_settable(L, -3);
861 lua_settable(L, -3);
864 else {
865 lua_pushnil(L);
867 return 1;
870 /*luadoc
871 @function model.setCurve(curve, params)
873 Set Curve parameters
875 @param curve (unsigned number) curve number (use 0 for Curve1)
877 @param params see model.getCurve return format for table format. setCurve uses standard
878 lua array indexing and arrays start at index 1
880 The first and last x value must -100 and 100 and x values must be monotonically increasing
882 @retval 0 - Everything okay
883 1 - Wrong number of points
884 2 - Invalid Curve number
885 3 - Cuve does not fit anymore
886 4 - point of out of index
887 5 - x value not monotonically increasing
888 6 - y value not in range [-100;100]
889 7 - extra values for y are set
890 8 - extra values for x are set
892 @status current Introduced in 2.2.0
894 Example setting a 4-point custom curve:
895 ```lua
896 params = {}
897 params["x"] = {-100, -34, 77, 100}
898 params["y"] = {-70, 20, -89, -100}
899 params["smooth"] = true
900 params["type"] = 1
901 val = model.setCurve(2, params)
903 setting a 6-point standard smoothed curve
904 ```lua
905 val = model.setCurve(3, {smooth=true, y={-100, -50, 0, 50, 100, 80}})
909 static int luaModelSetCurve(lua_State *L)
911 unsigned int curveIdx = luaL_checkunsigned(L, 1);
913 if (curveIdx >= MAX_CURVES) {
914 lua_pushinteger(L, 2);
915 return 1;
917 int8_t xPoints[MAX_POINTS_PER_CURVE];
918 int8_t yPoints[MAX_POINTS_PER_CURVE];
920 // Init to invalid values
921 memset(xPoints, -127, sizeof(xPoints));
922 memset(yPoints, -127, sizeof(yPoints));
925 CurveData &destCurveData = g_model.curves[curveIdx];
926 CurveData newCurveData;
927 memclear(&newCurveData, sizeof(CurveData));
929 luaL_checktype(L, -1, LUA_TTABLE);
930 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
931 luaL_checktype(L, -2, LUA_TSTRING); // key is string
932 const char *key = luaL_checkstring(L, -2);
933 if (!strcmp(key, "name")) {
934 const char *name = luaL_checkstring(L, -1);
935 str2zchar(newCurveData.name, name, sizeof(newCurveData.name));
937 else if (!strcmp(key, "type")) {
938 newCurveData.type = luaL_checkinteger(L, -1);
940 else if (!strcmp(key, "smooth")) {
941 // Earlier version of this api expected a 0/1 integer instead of a boolean
942 // Still accept a 0/1 here
943 if (lua_isboolean(L,-1))
944 newCurveData.smooth = lua_toboolean(L, -1);
945 else
946 newCurveData.smooth = luaL_checkinteger(L, -1);
948 else if (!strcmp(key, "x") || !strcmp(key, "y")) {
949 luaL_checktype(L, -1, LUA_TTABLE);
950 bool isX = !strcmp(key, "x");
952 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
953 int idx = luaL_checkinteger(L, -2)-1;
954 if (idx < 0 || idx > MAX_POINTS_PER_CURVE) {
955 lua_pushinteger(L, 4);
956 return 1;
958 int8_t val = luaL_checkinteger(L, -1);
959 if (val < -100 || val > 100) {
960 lua_pushinteger(L, 6);
961 return 1;
963 if (isX)
964 xPoints[idx] = val;
965 else
966 yPoints[idx] = val;
970 // Check how many points are set
971 uint8_t numPoints=0;
972 do {
973 numPoints++;
974 } while (yPoints[numPoints]!=-127 && numPoints < MAX_POINTS_PER_CURVE);
975 newCurveData.points = numPoints - 5;
977 if (numPoints < MIN_POINTS_PER_CURVE || numPoints > MAX_POINTS_PER_CURVE) {
978 lua_pushinteger(L, 1);
979 return 1;
982 if (newCurveData.type == CURVE_TYPE_CUSTOM) {
984 // The rest of the points are checked by the monotonic condition
985 for (unsigned int i=numPoints; i < sizeof(xPoints);i++)
987 if (xPoints[i] != -127)
989 lua_pushinteger(L, 8);
990 return 1;
994 // Check first and last point
995 if (xPoints[0] != -100 || xPoints[newCurveData.points + 4] != 100) {
996 lua_pushinteger(L, 5);
997 return 1;
1000 // Check that x values are increasing
1001 for (int i = 1; i < numPoints; i++) {
1002 if (xPoints[i - 1] > xPoints[i]) {
1003 lua_pushinteger(L, 5);
1004 return 1;
1009 // Check that ypoints have the right number of points set
1010 for (int i=0; i < 5 + newCurveData.points;i++)
1012 if (yPoints[i] == -127)
1014 lua_pushinteger(L, 7);
1015 return 1;
1019 // Calculate size of curve we replace
1020 int oldCurveMemSize;
1021 if (destCurveData.type == CURVE_TYPE_STANDARD) {
1022 oldCurveMemSize = 5 + destCurveData.points;
1024 else {
1025 oldCurveMemSize = 8 + 2 * destCurveData.points;
1028 // Calculate own size
1029 int newCurveMemSize;
1030 if (newCurveData.type == CURVE_TYPE_STANDARD)
1031 newCurveMemSize = 5 + newCurveData.points;
1032 else
1033 newCurveMemSize = 8 + 2 * newCurveData.points;
1035 int shift = newCurveMemSize - oldCurveMemSize;
1037 // Also checks if new curve size would fit
1038 if (!moveCurve(curveIdx, shift)) {
1039 lua_pushinteger(L, 3);
1040 TRACE("curve shift is %d", shift);
1041 return 1;
1044 // Curve fits into mem, fill new curve
1045 destCurveData = newCurveData;
1047 int8_t *point = curveAddress(curveIdx);
1048 for (int i = 0; i < destCurveData.points + 5; i++) {
1049 *point++ = yPoints[i];
1052 if (destCurveData.type == CURVE_TYPE_CUSTOM) {
1053 for (int i = 1; i < destCurveData.points + 4; i++) {
1054 *point++ = xPoints[i];
1057 storageDirty(EE_MODEL);
1059 lua_pushinteger(L, 0);
1060 return 1;
1063 /*luadoc
1064 @function model.getCustomFunction(function)
1066 Get Custom Function parameters
1068 @param function (unsigned number) custom function number (use 0 for CF1)
1070 @retval nil requested custom function does not exist
1072 @retval table custom function data:
1073 * `switch` (number) switch index
1074 * `func` (number) function index
1075 * `name` (string) Name of track to play (only returned only returned if action is play track, sound or script)
1076 * `value` (number) value (only returned only returned if action is **not** play track, sound or script)
1077 * `mode` (number) mode (only returned only returned if action is **not** play track, sound or script)
1078 * `param` (number) parameter (only returned only returned if action is **not** play track, sound or script)
1079 * `active` (number) 0 = disabled, 1 = enabled
1081 @status current Introduced in 2.0.0, TODO rename function
1083 static int luaModelGetCustomFunction(lua_State *L)
1085 unsigned int idx = luaL_checkunsigned(L, 1);
1086 if (idx < MAX_SPECIAL_FUNCTIONS) {
1087 CustomFunctionData * cfn = &g_model.customFn[idx];
1088 lua_newtable(L);
1089 lua_pushtableinteger(L, "switch", CFN_SWITCH(cfn));
1090 lua_pushtableinteger(L, "func", CFN_FUNC(cfn));
1091 if (CFN_FUNC(cfn) == FUNC_PLAY_TRACK || CFN_FUNC(cfn) == FUNC_BACKGND_MUSIC || CFN_FUNC(cfn) == FUNC_PLAY_SCRIPT) {
1092 lua_pushtablenzstring(L, "name", cfn->play.name);
1094 else {
1095 lua_pushtableinteger(L, "value", cfn->all.val);
1096 lua_pushtableinteger(L, "mode", cfn->all.mode);
1097 lua_pushtableinteger(L, "param", cfn->all.param);
1099 lua_pushtableinteger(L, "active", CFN_ACTIVE(cfn));
1101 else {
1102 lua_pushnil(L);
1104 return 1;
1107 /*luadoc
1108 @function model.setCustomFunction(function, value)
1110 Set Custom Function parameters
1112 @param function (unsigned number) custom function number (use 0 for CF1)
1114 @param value (table) custom function parameters, see model.getCustomFunction() for table format
1116 @notice If a parameter is missing from the value, then
1117 that parameter remains unchanged.
1119 @status current Introduced in 2.0.0, TODO rename function
1121 static int luaModelSetCustomFunction(lua_State *L)
1123 unsigned int idx = luaL_checkunsigned(L, 1);
1124 if (idx < MAX_SPECIAL_FUNCTIONS) {
1125 CustomFunctionData * cfn = &g_model.customFn[idx];
1126 memclear(cfn, sizeof(CustomFunctionData));
1127 luaL_checktype(L, -1, LUA_TTABLE);
1128 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
1129 luaL_checktype(L, -2, LUA_TSTRING); // key is string
1130 const char * key = luaL_checkstring(L, -2);
1131 if (!strcmp(key, "switch")) {
1132 CFN_SWITCH(cfn) = luaL_checkinteger(L, -1);
1134 else if (!strcmp(key, "func")) {
1135 CFN_FUNC(cfn) = luaL_checkinteger(L, -1);
1137 else if (!strcmp(key, "name")) {
1138 const char * name = luaL_checkstring(L, -1);
1139 strncpy(cfn->play.name, name, sizeof(cfn->play.name));
1141 else if (!strcmp(key, "value")) {
1142 cfn->all.val = luaL_checkinteger(L, -1);
1144 else if (!strcmp(key, "mode")) {
1145 cfn->all.mode = luaL_checkinteger(L, -1);
1147 else if (!strcmp(key, "param")) {
1148 cfn->all.param = luaL_checkinteger(L, -1);
1150 else if (!strcmp(key, "active")) {
1151 CFN_ACTIVE(cfn) = luaL_checkinteger(L, -1);
1154 storageDirty(EE_MODEL);
1157 return 0;
1160 /*luadoc
1161 @function model.getOutput(index)
1163 Get servo parameters
1165 @param index (unsigned number) output number (use 0 for CH1)
1167 @retval nil requested output does not exist
1169 @retval table output parameters:
1170 * `name` (string) name
1171 * `min` (number) Minimum % * 10
1172 * `max` (number) Maximum % * 10
1173 * `offset` (number) Subtrim * 10
1174 * `ppmCenter` (number) offset from PPM Center. 0 = 1500
1175 * `symetrical` (number) linear Subtrim 0 = Off, 1 = On
1176 * `revert` (number) irection 0 = ­­­---, 1 = INV
1177 * `curve`
1178 * (number) Curve number (0 for Curve1)
1179 * or `nil` if no curve set
1181 @status current Introduced in 2.0.0
1183 static int luaModelGetOutput(lua_State *L)
1185 unsigned int idx = luaL_checkunsigned(L, 1);
1186 if (idx < MAX_OUTPUT_CHANNELS) {
1187 LimitData * limit = limitAddress(idx);
1188 lua_newtable(L);
1189 lua_pushtablezstring(L, "name", limit->name);
1190 lua_pushtableinteger(L, "min", limit->min-1000);
1191 lua_pushtableinteger(L, "max", limit->max+1000);
1192 lua_pushtableinteger(L, "offset", limit->offset);
1193 lua_pushtableinteger(L, "ppmCenter", limit->ppmCenter);
1194 lua_pushtableinteger(L, "symetrical", limit->symetrical);
1195 lua_pushtableinteger(L, "revert", limit->revert);
1196 if (limit->curve)
1197 lua_pushtableinteger(L, "curve", limit->curve-1);
1199 else {
1200 lua_pushnil(L);
1202 return 1;
1205 /*luadoc
1206 @function model.setOutput(index, value)
1208 Set servo parameters
1210 @param index (unsigned number) channel number (use 0 for CH1)
1212 @param value (table) servo parameters, see model.getOutput() for table format
1214 @notice If a parameter is missing from the value, then
1215 that parameter remains unchanged.
1217 @status current Introduced in 2.0.0
1219 static int luaModelSetOutput(lua_State *L)
1221 unsigned int idx = luaL_checkunsigned(L, 1);
1222 if (idx < MAX_OUTPUT_CHANNELS) {
1223 LimitData * limit = limitAddress(idx);
1224 memclear(limit, sizeof(LimitData));
1225 luaL_checktype(L, -1, LUA_TTABLE);
1226 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
1227 luaL_checktype(L, -2, LUA_TSTRING); // key is string
1228 const char * key = luaL_checkstring(L, -2);
1229 if (!strcmp(key, "name")) {
1230 const char * name = luaL_checkstring(L, -1);
1231 str2zchar(limit->name, name, sizeof(limit->name));
1233 else if (!strcmp(key, "min")) {
1234 limit->min = luaL_checkinteger(L, -1)+1000;
1236 else if (!strcmp(key, "max")) {
1237 limit->max = luaL_checkinteger(L, -1)-1000;
1239 else if (!strcmp(key, "offset")) {
1240 limit->offset = luaL_checkinteger(L, -1);
1242 else if (!strcmp(key, "ppmCenter")) {
1243 limit->ppmCenter = luaL_checkinteger(L, -1);
1245 else if (!strcmp(key, "symetrical")) {
1246 limit->symetrical = luaL_checkinteger(L, -1);
1248 else if (!strcmp(key, "revert")) {
1249 limit->revert = luaL_checkinteger(L, -1);
1251 else if (!strcmp(key, "curve")) {
1252 limit->curve = luaL_checkinteger(L, -1) + 1;
1255 storageDirty(EE_MODEL);
1258 return 0;
1261 /*luadoc
1262 @function model.getGlobalVariable(index [, flight_mode])
1264 Return current global variable value
1266 @notice a simple warning or notice
1268 @param index zero based global variable index, use 0 for GV1, 8 for GV9
1270 @param flight_mode Flight mode number (0 = FM0, 8 = FM8)
1272 @retval nil requested global variable does not exist
1274 @retval number current value of global variable
1276 Example:
1278 ```lua
1279 -- get GV3 (index = 2) from Flight mode 0 (FM0)
1280 val = model.getGlobalVariable(2, 0)
1283 static int luaModelGetGlobalVariable(lua_State *L)
1285 unsigned int idx = luaL_checkunsigned(L, 1);
1286 unsigned int phase = luaL_checkunsigned(L, 2);
1287 if (phase < MAX_FLIGHT_MODES && idx < MAX_GVARS)
1288 lua_pushinteger(L, g_model.flightModeData[phase].gvars[idx]);
1289 else
1290 lua_pushnil(L);
1291 return 1;
1294 /*luadoc
1295 @function model.setGlobalVariable(index, flight_mode, value)
1297 Sets current global variable value. See also model.getGlobalVariable()
1299 @param index zero based global variable index, use 0 for GV1, 8 for GV9
1301 @param flight_mode Flight mode number (0 = FM0, 8 = FM8)
1303 @param value new value for global variable. Permitted range is
1304 from -1024 to 1024.
1306 @notice Global variable can only store integer values,
1307 any floating point value is converted into integer value
1308 by truncating everything behind a floating point.
1310 static int luaModelSetGlobalVariable(lua_State *L)
1312 unsigned int idx = luaL_checkunsigned(L, 1);
1313 unsigned int phase = luaL_checkunsigned(L, 2);
1314 int value = luaL_checkinteger(L, 3);
1315 if (phase < MAX_FLIGHT_MODES && idx < MAX_GVARS && value >= -GVAR_MAX && value <= GVAR_MAX) {
1316 g_model.flightModeData[phase].gvars[idx] = value;
1317 storageDirty(EE_MODEL);
1319 return 0;
1322 /*luadoc
1323 @function model.getSensor(sensor)
1325 Get Telemetry Sensor parameters
1327 @param sensor (unsigned number) sensor number (use 0 for sensor 1)
1329 @retval nil requested logical switch does not exist
1331 @retval table logical switch data:
1332 * `func` (number) function index
1333 * `v1` (number) V1 value (index)
1334 * `v2` (number) V2 value (index or value)
1335 * `v3` (number) V3 value (index or value)
1336 * `and` (number) AND switch index
1337 * `delay` (number) delay (time in 1/10 s)
1338 * `duration` (number) duration (time in 1/10 s)
1340 @status current Introduced in 2.3.0
1342 static int luaModelGetSensor(lua_State *L)
1344 unsigned int idx = luaL_checkunsigned(L, 1);
1345 if (idx < MAX_TELEMETRY_SENSORS) {
1346 TelemetrySensor & sensor = g_model.telemetrySensors[idx];
1347 lua_newtable(L);
1348 lua_pushtableinteger(L, "type", sensor.type);
1349 lua_pushtablezstring(L, "name", sensor.label);
1350 lua_pushtableinteger(L, "unit", sensor.unit);
1351 lua_pushtableinteger(L, "prec", sensor.prec);
1352 if (sensor.type == TELEM_TYPE_CUSTOM) {
1353 lua_pushtableinteger(L, "id", sensor.id);
1354 lua_pushtableinteger(L, "instance", sensor.instance);
1356 else {
1357 lua_pushtableinteger(L, "formula", sensor.formula);
1360 else {
1361 lua_pushnil(L);
1363 return 1;
1366 const luaL_Reg modelLib[] = {
1367 { "getInfo", luaModelGetInfo },
1368 { "setInfo", luaModelSetInfo },
1369 { "getModule", luaModelGetModule },
1370 { "setModule", luaModelSetModule },
1371 { "getTimer", luaModelGetTimer },
1372 { "setTimer", luaModelSetTimer },
1373 { "resetTimer", luaModelResetTimer },
1374 { "getInputsCount", luaModelGetInputsCount },
1375 { "getInput", luaModelGetInput },
1376 { "insertInput", luaModelInsertInput },
1377 { "deleteInput", luaModelDeleteInput },
1378 { "deleteInputs", luaModelDeleteInputs },
1379 { "defaultInputs", luaModelDefaultInputs },
1380 { "getMixesCount", luaModelGetMixesCount },
1381 { "getMix", luaModelGetMix },
1382 { "insertMix", luaModelInsertMix },
1383 { "deleteMix", luaModelDeleteMix },
1384 { "deleteMixes", luaModelDeleteMixes },
1385 { "getLogicalSwitch", luaModelGetLogicalSwitch },
1386 { "setLogicalSwitch", luaModelSetLogicalSwitch },
1387 { "getCustomFunction", luaModelGetCustomFunction },
1388 { "setCustomFunction", luaModelSetCustomFunction },
1389 { "getCurve", luaModelGetCurve },
1390 { "setCurve", luaModelSetCurve },
1391 { "getOutput", luaModelGetOutput },
1392 { "setOutput", luaModelSetOutput },
1393 { "getGlobalVariable", luaModelGetGlobalVariable },
1394 { "setGlobalVariable", luaModelSetGlobalVariable },
1395 { "getSensor", luaModelGetSensor },
1396 { NULL, NULL } /* sentinel */