Fix doc path
[opentx.git] / radio / src / lua / api_model.cpp
blob2477c9d22c3b5fcaeb6f288f9d4c44d61923be7b
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 `rfProtocol` 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 * `rfProtocol` (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, "rfProtocol", module.rfProtocol);
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, "rfProtocol")) {
149 module.rfProtocol = 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
337 @status current Introduced in 2.0.0, `switch` added in TODO
339 static int luaModelGetInput(lua_State *L)
341 unsigned int chn = luaL_checkunsigned(L, 1);
342 unsigned int idx = luaL_checkunsigned(L, 2);
343 unsigned int first = getFirstInput(chn);
344 unsigned int count = getInputsCountFromFirst(chn, first);
345 if (idx < count) {
346 ExpoData * expo = expoAddress(first+idx);
347 lua_newtable(L);
348 lua_pushtablezstring(L, "name", expo->name);
349 lua_pushtableinteger(L, "source", expo->srcRaw);
350 lua_pushtableinteger(L, "weight", expo->weight);
351 lua_pushtableinteger(L, "offset", expo->offset);
352 lua_pushtableinteger(L, "switch", expo->swtch);
354 else {
355 lua_pushnil(L);
357 return 1;
360 /*luadoc
361 @function model.insertInput(input, line, value)
363 Insert an Input at specified line
365 @param input (unsigned number) input number (use 0 for Input1)
367 @param line (unsigned number) input line (use 0 for first line)
369 @param value (table) input data, see model.getInput()
371 @status current Introduced in 2.0.0, `switch` added in TODO
373 static int luaModelInsertInput(lua_State *L)
375 unsigned int chn = luaL_checkunsigned(L, 1);
376 unsigned int idx = luaL_checkunsigned(L, 2);
378 unsigned int first = getFirstInput(chn);
379 unsigned int count = getInputsCountFromFirst(chn, first);
381 if (chn<MAX_INPUTS && getExposCount()<MAX_EXPOS && idx<=count) {
382 idx = first + idx;
383 s_currCh = chn + 1;
384 insertExpo(idx);
385 ExpoData * expo = expoAddress(idx);
386 luaL_checktype(L, -1, LUA_TTABLE);
387 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
388 luaL_checktype(L, -2, LUA_TSTRING); // key is string
389 const char * key = luaL_checkstring(L, -2);
390 if (!strcmp(key, "name")) {
391 const char * name = luaL_checkstring(L, -1);
392 str2zchar(expo->name, name, sizeof(expo->name));
394 else if (!strcmp(key, "source")) {
395 expo->srcRaw = luaL_checkinteger(L, -1);
397 else if (!strcmp(key, "weight")) {
398 expo->weight = luaL_checkinteger(L, -1);
400 else if (!strcmp(key, "offset")) {
401 expo->offset = luaL_checkinteger(L, -1);
403 else if (!strcmp(key, "switch")) {
404 expo->swtch = luaL_checkinteger(L, -1);
409 return 0;
412 /*luadoc
413 @function model.deleteInput(input, line)
415 Delete line from specified input
417 @param input (unsigned number) input number (use 0 for Input1)
419 @param line (unsigned number) input line (use 0 for first line)
421 @status current Introduced in 2.0.0
423 static int luaModelDeleteInput(lua_State *L)
425 unsigned int chn = luaL_checkunsigned(L, 1);
426 unsigned int idx = luaL_checkunsigned(L, 2);
428 int first = getFirstInput(chn);
429 unsigned int count = getInputsCountFromFirst(chn, first);
431 if (idx < count) {
432 deleteExpo(first+idx);
435 return 0;
438 /*luadoc
439 @function model.deleteInputs()
441 Delete all Inputs
443 @status current Introduced in 2.0.0
445 static int luaModelDeleteInputs(lua_State *L)
447 clearInputs();
448 return 0;
451 /*luadoc
452 @function model.defaultInputs()
454 Set all inputs to defaults
456 @status current Introduced in 2.0.0
458 static int luaModelDefaultInputs(lua_State *L)
460 defaultInputs();
461 return 0;
464 static unsigned int getFirstMix(unsigned int chn)
466 for (unsigned int i=0; i<MAX_MIXERS; i++) {
467 MixData * mix = mixAddress(i);
468 if (!mix->srcRaw || mix->destCh>=chn) {
469 return i;
472 return 0;
475 static unsigned int getMixesCountFromFirst(unsigned int chn, unsigned int first)
477 unsigned int count = 0;
478 for (unsigned int i=first; i<MAX_MIXERS; i++) {
479 MixData * mix = mixAddress(i);
480 if (!mix->srcRaw || mix->destCh!=chn) break;
481 count++;
483 return count;
486 static unsigned int getMixesCount(unsigned int chn)
488 return getMixesCountFromFirst(chn, getFirstMix(chn));
491 /*luadoc
492 @function model.getMixesCount(channel)
494 Get the number of Mixer lines that the specified Channel has
496 @param channel (unsigned number) channel number (use 0 for CH1)
498 @retval number number of mixes for requested channel
500 @status current Introduced in 2.0.0
502 static int luaModelGetMixesCount(lua_State *L)
504 unsigned int chn = luaL_checkunsigned(L, 1);
505 unsigned int count = getMixesCount(chn);
506 lua_pushinteger(L, count);
507 return 1;
510 /*luadoc
511 @function model.getMix(channel, line)
513 Get configuration for specified Mix
515 @param channel (unsigned number) channel number (use 0 for CH1)
517 @param line (unsigned number) mix number (use 0 for first line(mix))
519 @retval nil requested channel or line does not exist
521 @retval table mix data:
522 * `name` (string) mix line name
523 * `source` (number) source index
524 * `weight` (number) weight (1024 == 100%) value or GVAR1..9 = 4096..4011, -GVAR1..9 = 4095..4087
525 * `offset` (number) offset value or GVAR1..9 = 4096..4011, -GVAR1..9 = 4095..4087
526 * `switch` (number) switch index
527 * `multiplex` (number) multiplex (0 = ADD, 1 = MULTIPLY, 2 = REPLACE)
528 * `curveType` (number) curve type (function, expo, custom curve)
529 * `curveValue` (number) curve index
530 * `flightModes` (number) bit-mask of active flight modes
531 * `carryTrim` (boolean) carry trim
532 * `mixWarn` (number) warning (0 = off, 1 = 1 beep, .. 3 = 3 beeps)
533 * `delayUp` (number) delay up (time in 1/10 s)
534 * `delayDown` (number) delay down
535 * `speedUp` (number) speed up
536 * `speedDown` (number) speed down
538 @status current Introduced in 2.0.0, parameters below `multiplex` added in 2.0.13
540 static int luaModelGetMix(lua_State *L)
542 unsigned int chn = luaL_checkunsigned(L, 1);
543 unsigned int idx = luaL_checkunsigned(L, 2);
544 unsigned int first = getFirstMix(chn);
545 unsigned int count = getMixesCountFromFirst(chn, first);
546 if (idx < count) {
547 MixData * mix = mixAddress(first+idx);
548 lua_newtable(L);
549 lua_pushtablezstring(L, "name", mix->name);
550 lua_pushtableinteger(L, "source", mix->srcRaw);
551 lua_pushtableinteger(L, "weight", mix->weight);
552 lua_pushtableinteger(L, "offset", mix->offset);
553 lua_pushtableinteger(L, "switch", mix->swtch);
554 lua_pushtableinteger(L, "curveType", mix->curve.type);
555 lua_pushtableinteger(L, "curveValue", mix->curve.value);
556 lua_pushtableinteger(L, "multiplex", mix->mltpx);
557 lua_pushtableinteger(L, "flightModes", mix->flightModes);
558 lua_pushtableboolean(L, "carryTrim", mix->carryTrim);
559 lua_pushtableinteger(L, "mixWarn", mix->mixWarn);
560 lua_pushtableinteger(L, "delayUp", mix->delayUp);
561 lua_pushtableinteger(L, "delayDown", mix->delayDown);
562 lua_pushtableinteger(L, "speedUp", mix->speedUp);
563 lua_pushtableinteger(L, "speedDown", mix->speedDown);
565 else {
566 lua_pushnil(L);
568 return 1;
571 /*luadoc
572 @function model.insertMix(channel, line, value)
574 Insert a mixer line into Channel
576 @param channel (unsigned number) channel number (use 0 for CH1)
578 @param line (unsigned number) mix number (use 0 for first line(mix))
580 @param value (table) see model.getMix() for table format
582 @status current Introduced in 2.0.0, parameters below `multiplex` added in 2.0.13
584 static int luaModelInsertMix(lua_State *L)
586 unsigned int chn = luaL_checkunsigned(L, 1);
587 unsigned int idx = luaL_checkunsigned(L, 2);
589 unsigned int first = getFirstMix(chn);
590 unsigned int count = getMixesCountFromFirst(chn, first);
592 if (chn<MAX_OUTPUT_CHANNELS && getMixesCount()<MAX_MIXERS && idx<=count) {
593 idx += first;
594 s_currCh = chn+1;
595 insertMix(idx);
596 MixData *mix = mixAddress(idx);
597 luaL_checktype(L, -1, LUA_TTABLE);
598 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
599 luaL_checktype(L, -2, LUA_TSTRING); // key is string
600 const char * key = luaL_checkstring(L, -2);
601 if (!strcmp(key, "name")) {
602 const char * name = luaL_checkstring(L, -1);
603 str2zchar(mix->name, name, sizeof(mix->name));
605 else if (!strcmp(key, "source")) {
606 mix->srcRaw = luaL_checkinteger(L, -1);
608 else if (!strcmp(key, "weight")) {
609 mix->weight = luaL_checkinteger(L, -1);
611 else if (!strcmp(key, "offset")) {
612 mix->offset = luaL_checkinteger(L, -1);
614 else if (!strcmp(key, "switch")) {
615 mix->swtch = luaL_checkinteger(L, -1);
617 else if (!strcmp(key, "curveType")) {
618 mix->curve.type = luaL_checkinteger(L, -1);
620 else if (!strcmp(key, "curveValue")) {
621 mix->curve.value = luaL_checkinteger(L, -1);
623 else if (!strcmp(key, "multiplex")) {
624 mix->mltpx = luaL_checkinteger(L, -1);
626 else if (!strcmp(key, "flightModes")) {
627 mix->flightModes = luaL_checkinteger(L, -1);
629 else if (!strcmp(key, "carryTrim")) {
630 mix->carryTrim = lua_toboolean(L, -1);
632 else if (!strcmp(key, "mixWarn")) {
633 mix->mixWarn = luaL_checkinteger(L, -1);
635 else if (!strcmp(key, "delayUp")) {
636 mix->delayUp = luaL_checkinteger(L, -1);
638 else if (!strcmp(key, "delayDown")) {
639 mix->delayDown = luaL_checkinteger(L, -1);
641 else if (!strcmp(key, "speedUp")) {
642 mix->speedUp = luaL_checkinteger(L, -1);
644 else if (!strcmp(key, "speedDown")) {
645 mix->speedDown = luaL_checkinteger(L, -1);
650 return 0;
653 /*luadoc
654 @function model.deleteMix(channel, line)
656 Delete mixer line from specified Channel
658 @param channel (unsigned number) channel number (use 0 for CH1)
660 @param line (unsigned number) mix number (use 0 for first line(mix))
662 @status current Introduced in 2.0.0
664 static int luaModelDeleteMix(lua_State *L)
666 unsigned int chn = luaL_checkunsigned(L, 1);
667 unsigned int idx = luaL_checkunsigned(L, 2);
669 unsigned int first = getFirstMix(chn);
670 unsigned int count = getMixesCountFromFirst(chn, first);
672 if (idx < count) {
673 deleteMix(first+idx);
676 return 0;
679 /*luadoc
680 @function model.deleteMixes()
682 Remove all mixers
684 @status current Introduced in 2.0.0
686 static int luaModelDeleteMixes(lua_State *L)
688 memset(g_model.mixData, 0, sizeof(g_model.mixData));
689 return 0;
692 /*luadoc
693 @function model.getLogicalSwitch(switch)
695 Get Logical Switch parameters
697 @param switch (unsigned number) logical switch number (use 0 for LS1)
699 @retval nil requested logical switch does not exist
701 @retval table logical switch data:
702 * `func` (number) function index
703 * `v1` (number) V1 value (index)
704 * `v2` (number) V2 value (index or value)
705 * `v3` (number) V3 value (index or value)
706 * `and` (number) AND switch index
707 * `delay` (number) delay (time in 1/10 s)
708 * `duration` (number) duration (time in 1/10 s)
710 @status current Introduced in 2.0.0
712 static int luaModelGetLogicalSwitch(lua_State *L)
714 unsigned int idx = luaL_checkunsigned(L, 1);
715 if (idx < MAX_LOGICAL_SWITCHES) {
716 LogicalSwitchData * sw = lswAddress(idx);
717 lua_newtable(L);
718 lua_pushtableinteger(L, "func", sw->func);
719 lua_pushtableinteger(L, "v1", sw->v1);
720 lua_pushtableinteger(L, "v2", sw->v2);
721 lua_pushtableinteger(L, "v3", sw->v3);
722 lua_pushtableinteger(L, "and", sw->andsw);
723 lua_pushtableinteger(L, "delay", sw->delay);
724 lua_pushtableinteger(L, "duration", sw->duration);
726 else {
727 lua_pushnil(L);
729 return 1;
732 /*luadoc
733 @function model.setLogicalSwitch(switch, value)
735 Set Logical Switch parameters
737 @param switch (unsigned number) logical switch number (use 0 for LS1)
739 @param value (table) see model.getLogicalSwitch() for table format
741 @notice If a parameter is missing from the value, then
742 that parameter remains unchanged.
744 @notice To set the `and` member (which is Lua keyword)
745 use the following syntax: `model.setLogicalSwitch(30, {func=4,v1=1,v2=-99, ["and"]=24})`
747 @status current Introduced in 2.0.0
749 static int luaModelSetLogicalSwitch(lua_State *L)
751 unsigned int idx = luaL_checkunsigned(L, 1);
752 if (idx < MAX_LOGICAL_SWITCHES) {
753 LogicalSwitchData * sw = lswAddress(idx);
754 memclear(sw, sizeof(LogicalSwitchData));
755 luaL_checktype(L, -1, LUA_TTABLE);
756 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
757 luaL_checktype(L, -2, LUA_TSTRING); // key is string
758 const char * key = luaL_checkstring(L, -2);
759 if (!strcmp(key, "func")) {
760 sw->func = luaL_checkinteger(L, -1);
762 else if (!strcmp(key, "v1")) {
763 sw->v1 = luaL_checkinteger(L, -1);
765 else if (!strcmp(key, "v2")) {
766 sw->v2 = luaL_checkinteger(L, -1);
768 else if (!strcmp(key, "v3")) {
769 sw->v3 = luaL_checkinteger(L, -1);
771 else if (!strcmp(key, "and")) {
772 sw->andsw = luaL_checkinteger(L, -1);
774 else if (!strcmp(key, "delay")) {
775 sw->delay = luaL_checkinteger(L, -1);
777 else if (!strcmp(key, "duration")) {
778 sw->duration = luaL_checkinteger(L, -1);
781 storageDirty(EE_MODEL);
784 return 0;
787 /*luadoc
788 @function model.getCurve(curve)
790 Get Curve parameters
792 @param curve (unsigned number) curve number (use 0 for Curve1)
794 @retval nil requested curve does not exist
796 @retval table curve data:
797 * `name` (string) name
798 * `type` (number) type
799 * `smooth` (boolean) smooth
800 * `points` (number) number of points
801 * `y` (table) table of Y values:
802 * `key` is point number (zero based)
803 * `value` is y value
804 * `x` (table) **only included for custom curve type**:
805 * `key` is point number (zero based)
806 * `value` is x value
808 Note that functions returns the tables starting with index 0 contrary to LUA's
809 usual index starting with 1
811 @status current Introduced in 2.0.12
813 static int luaModelGetCurve(lua_State *L)
815 unsigned int idx = luaL_checkunsigned(L, 1);
816 if (idx < MAX_CURVES) {
817 CurveData & curveData = g_model.curves[idx];
818 lua_newtable(L);
819 lua_pushtablezstring(L, "name", curveData.name);
820 lua_pushtableinteger(L, "type", curveData.type);
821 lua_pushtableboolean(L, "smooth", curveData.smooth);
822 lua_pushtableinteger(L, "points", curveData.points + 5);
823 lua_pushstring(L, "y");
824 lua_newtable(L);
825 int8_t * point = curveAddress(idx);
826 for (int i=0; i < curveData.points + 5; i++) {
827 lua_pushinteger(L, i);
828 lua_pushinteger(L, *point++);
829 lua_settable(L, -3);
831 lua_settable(L, -3);
832 if (curveData.type == CURVE_TYPE_CUSTOM) {
833 lua_pushstring(L, "x");
834 lua_newtable(L);
835 lua_pushinteger(L, 0);
836 lua_pushinteger(L, -100);
837 lua_settable(L, -3);
838 for (int i=0; i < curveData.points + 3; i++) {
839 lua_pushinteger(L, i+1);
840 lua_pushinteger(L, *point++);
841 lua_settable(L, -3);
843 lua_pushinteger(L, curveData.points + 4);
844 lua_pushinteger(L, 100);
845 lua_settable(L, -3);
846 lua_settable(L, -3);
849 else {
850 lua_pushnil(L);
852 return 1;
855 /*luadoc
856 @function model.setCurve(curve, params)
858 Set Curve parameters
860 @param curve (unsigned number) curve number (use 0 for Curve1)
862 @param params see model.getCurve return format for table format. setCurve uses standard
863 lua array indexing and arrays start at index 1
865 The first and last x value must -100 and 100 and x values must be monotonically increasing
867 @retval 0 - Everything okay
868 1 - Wrong number of points
869 2 - Invalid Curve number
870 3 - Cuve does not fit anymore
871 4 - point of out of index
872 5 - x value not monotonically increasing
873 6 - y value not in range [-100;100]
874 7 - extra values for y are set
875 8 - extra values for x are set
877 @status current Introduced in 2.2.0
879 Example setting a 4-point custom curve:
880 ```lua
881 params = {}
882 params["x"] = {-100, -34, 77, 100}
883 params["y"] = {-70, 20, -89, -100}
884 params["smooth"] = true
885 params["type"] = 1
886 val = model.setCurve(2, params)
888 setting a 6-point standard smoothed curve
889 ```lua
890 val = model.setCurve(3, {smooth=true, y={-100, -50, 0, 50, 100, 80}})
894 static int luaModelSetCurve(lua_State *L)
896 unsigned int curveIdx = luaL_checkunsigned(L, 1);
898 if (curveIdx >= MAX_CURVES) {
899 lua_pushinteger(L, 2);
900 return 1;
902 int8_t xPoints[MAX_POINTS_PER_CURVE];
903 int8_t yPoints[MAX_POINTS_PER_CURVE];
905 // Init to invalid values
906 memset(xPoints, -127, sizeof(xPoints));
907 memset(yPoints, -127, sizeof(yPoints));
910 CurveData &destCurveData = g_model.curves[curveIdx];
911 CurveData newCurveData;
912 memclear(&newCurveData, sizeof(CurveData));
914 luaL_checktype(L, -1, LUA_TTABLE);
915 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
916 luaL_checktype(L, -2, LUA_TSTRING); // key is string
917 const char *key = luaL_checkstring(L, -2);
918 if (!strcmp(key, "name")) {
919 const char *name = luaL_checkstring(L, -1);
920 str2zchar(newCurveData.name, name, sizeof(newCurveData.name));
922 else if (!strcmp(key, "type")) {
923 newCurveData.type = luaL_checkinteger(L, -1);
925 else if (!strcmp(key, "smooth")) {
926 // Earlier version of this api expected a 0/1 integer instead of a boolean
927 // Still accept a 0/1 here
928 if (lua_isboolean(L,-1))
929 newCurveData.smooth = lua_toboolean(L, -1);
930 else
931 newCurveData.smooth = luaL_checkinteger(L, -1);
933 else if (!strcmp(key, "x") || !strcmp(key, "y")) {
934 luaL_checktype(L, -1, LUA_TTABLE);
935 bool isX = !strcmp(key, "x");
937 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
938 int idx = luaL_checkinteger(L, -2)-1;
939 if (idx < 0 || idx > MAX_POINTS_PER_CURVE) {
940 lua_pushinteger(L, 4);
941 return 1;
943 int8_t val = luaL_checkinteger(L, -1);
944 if (val < -100 || val > 100) {
945 lua_pushinteger(L, 6);
946 return 1;
948 if (isX)
949 xPoints[idx] = val;
950 else
951 yPoints[idx] = val;
955 // Check how many points are set
956 uint8_t numPoints=0;
957 do {
958 numPoints++;
959 } while (yPoints[numPoints]!=-127 && numPoints < MAX_POINTS_PER_CURVE);
960 newCurveData.points = numPoints - 5;
962 if (numPoints < MIN_POINTS_PER_CURVE || numPoints > MAX_POINTS_PER_CURVE) {
963 lua_pushinteger(L, 1);
964 return 1;
967 if (newCurveData.type == CURVE_TYPE_CUSTOM) {
969 // The rest of the points are checked by the monotonic condition
970 for (unsigned int i=numPoints; i < sizeof(xPoints);i++)
972 if (xPoints[i] != -127)
974 lua_pushinteger(L, 8);
975 return 1;
979 // Check first and last point
980 if (xPoints[0] != -100 || xPoints[newCurveData.points + 4] != 100) {
981 lua_pushinteger(L, 5);
982 return 1;
985 // Check that x values are increasing
986 for (int i = 1; i < numPoints; i++) {
987 if (xPoints[i - 1] > xPoints[i]) {
988 lua_pushinteger(L, 5);
989 return 1;
994 // Check that ypoints have the right number of points set
995 for (int i=0; i < 5 + newCurveData.points;i++)
997 if (yPoints[i] == -127)
999 lua_pushinteger(L, 7);
1000 return 1;
1004 // Calculate size of curve we replace
1005 int oldCurveMemSize;
1006 if (destCurveData.type == CURVE_TYPE_STANDARD) {
1007 oldCurveMemSize = 5 + destCurveData.points;
1009 else {
1010 oldCurveMemSize = 8 + 2 * destCurveData.points;
1013 // Calculate own size
1014 int newCurveMemSize;
1015 if (newCurveData.type == CURVE_TYPE_STANDARD)
1016 newCurveMemSize = 5 + newCurveData.points;
1017 else
1018 newCurveMemSize = 8 + 2 * newCurveData.points;
1020 int shift = newCurveMemSize - oldCurveMemSize;
1022 // Also checks if new curve size would fit
1023 if (!moveCurve(curveIdx, shift)) {
1024 lua_pushinteger(L, 3);
1025 TRACE("curve shift is %d", shift);
1026 return 1;
1029 // Curve fits into mem, fill new curve
1030 destCurveData = newCurveData;
1032 int8_t *point = curveAddress(curveIdx);
1033 for (int i = 0; i < destCurveData.points + 5; i++) {
1034 *point++ = yPoints[i];
1037 if (destCurveData.type == CURVE_TYPE_CUSTOM) {
1038 for (int i = 1; i < destCurveData.points + 4; i++) {
1039 *point++ = xPoints[i];
1042 storageDirty(EE_MODEL);
1044 lua_pushinteger(L, 0);
1045 return 1;
1048 /*luadoc
1049 @function model.getCustomFunction(function)
1051 Get Custom Function parameters
1053 @param function (unsigned number) custom function number (use 0 for CF1)
1055 @retval nil requested custom function does not exist
1057 @retval table custom function data:
1058 * `switch` (number) switch index
1059 * `func` (number) function index
1060 * `name` (string) Name of track to play (only returned only returned if action is play track, sound or script)
1061 * `value` (number) value (only returned only returned if action is **not** play track, sound or script)
1062 * `mode` (number) mode (only returned only returned if action is **not** play track, sound or script)
1063 * `param` (number) parameter (only returned only returned if action is **not** play track, sound or script)
1064 * `active` (number) 0 = disabled, 1 = enabled
1066 @status current Introduced in 2.0.0, TODO rename function
1068 static int luaModelGetCustomFunction(lua_State *L)
1070 unsigned int idx = luaL_checkunsigned(L, 1);
1071 if (idx < MAX_SPECIAL_FUNCTIONS) {
1072 CustomFunctionData * cfn = &g_model.customFn[idx];
1073 lua_newtable(L);
1074 lua_pushtableinteger(L, "switch", CFN_SWITCH(cfn));
1075 lua_pushtableinteger(L, "func", CFN_FUNC(cfn));
1076 if (CFN_FUNC(cfn) == FUNC_PLAY_TRACK || CFN_FUNC(cfn) == FUNC_BACKGND_MUSIC || CFN_FUNC(cfn) == FUNC_PLAY_SCRIPT) {
1077 lua_pushtablenzstring(L, "name", cfn->play.name);
1079 else {
1080 lua_pushtableinteger(L, "value", cfn->all.val);
1081 lua_pushtableinteger(L, "mode", cfn->all.mode);
1082 lua_pushtableinteger(L, "param", cfn->all.param);
1084 lua_pushtableinteger(L, "active", CFN_ACTIVE(cfn));
1086 else {
1087 lua_pushnil(L);
1089 return 1;
1092 /*luadoc
1093 @function model.setCustomFunction(function, value)
1095 Set Custom Function parameters
1097 @param function (unsigned number) custom function number (use 0 for CF1)
1099 @param value (table) custom function parameters, see model.getCustomFunction() for table format
1101 @notice If a parameter is missing from the value, then
1102 that parameter remains unchanged.
1104 @status current Introduced in 2.0.0, TODO rename function
1106 static int luaModelSetCustomFunction(lua_State *L)
1108 unsigned int idx = luaL_checkunsigned(L, 1);
1109 if (idx < MAX_SPECIAL_FUNCTIONS) {
1110 CustomFunctionData * cfn = &g_model.customFn[idx];
1111 memclear(cfn, sizeof(CustomFunctionData));
1112 luaL_checktype(L, -1, LUA_TTABLE);
1113 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
1114 luaL_checktype(L, -2, LUA_TSTRING); // key is string
1115 const char * key = luaL_checkstring(L, -2);
1116 if (!strcmp(key, "switch")) {
1117 CFN_SWITCH(cfn) = luaL_checkinteger(L, -1);
1119 else if (!strcmp(key, "func")) {
1120 CFN_FUNC(cfn) = luaL_checkinteger(L, -1);
1122 else if (!strcmp(key, "name")) {
1123 const char * name = luaL_checkstring(L, -1);
1124 strncpy(cfn->play.name, name, sizeof(cfn->play.name));
1126 else if (!strcmp(key, "value")) {
1127 cfn->all.val = luaL_checkinteger(L, -1);
1129 else if (!strcmp(key, "mode")) {
1130 cfn->all.mode = luaL_checkinteger(L, -1);
1132 else if (!strcmp(key, "param")) {
1133 cfn->all.param = luaL_checkinteger(L, -1);
1135 else if (!strcmp(key, "active")) {
1136 CFN_ACTIVE(cfn) = luaL_checkinteger(L, -1);
1139 storageDirty(EE_MODEL);
1142 return 0;
1145 /*luadoc
1146 @function model.getOutput(index)
1148 Get servo parameters
1150 @param index (unsigned number) output number (use 0 for CH1)
1152 @retval nil requested output does not exist
1154 @retval table output parameters:
1155 * `name` (string) name
1156 * `min` (number) Minimum % * 10
1157 * `max` (number) Maximum % * 10
1158 * `offset` (number) Subtrim * 10
1159 * `ppmCenter` (number) offset from PPM Center. 0 = 1500
1160 * `symetrical` (number) linear Subtrim 0 = Off, 1 = On
1161 * `revert` (number) irection 0 = ­­­---, 1 = INV
1162 * `curve`
1163 * (number) Curve number (0 for Curve1)
1164 * or `nil` if no curve set
1166 @status current Introduced in 2.0.0
1168 static int luaModelGetOutput(lua_State *L)
1170 unsigned int idx = luaL_checkunsigned(L, 1);
1171 if (idx < MAX_OUTPUT_CHANNELS) {
1172 LimitData * limit = limitAddress(idx);
1173 lua_newtable(L);
1174 lua_pushtablezstring(L, "name", limit->name);
1175 lua_pushtableinteger(L, "min", limit->min-1000);
1176 lua_pushtableinteger(L, "max", limit->max+1000);
1177 lua_pushtableinteger(L, "offset", limit->offset);
1178 lua_pushtableinteger(L, "ppmCenter", limit->ppmCenter);
1179 lua_pushtableinteger(L, "symetrical", limit->symetrical);
1180 lua_pushtableinteger(L, "revert", limit->revert);
1181 if (limit->curve)
1182 lua_pushtableinteger(L, "curve", limit->curve-1);
1184 else {
1185 lua_pushnil(L);
1187 return 1;
1190 /*luadoc
1191 @function model.setOutput(index, value)
1193 Set servo parameters
1195 @param index (unsigned number) channel number (use 0 for CH1)
1197 @param value (table) servo parameters, see model.getOutput() for table format
1199 @notice If a parameter is missing from the value, then
1200 that parameter remains unchanged.
1202 @status current Introduced in 2.0.0
1204 static int luaModelSetOutput(lua_State *L)
1206 unsigned int idx = luaL_checkunsigned(L, 1);
1207 if (idx < MAX_OUTPUT_CHANNELS) {
1208 LimitData * limit = limitAddress(idx);
1209 memclear(limit, sizeof(LimitData));
1210 luaL_checktype(L, -1, LUA_TTABLE);
1211 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
1212 luaL_checktype(L, -2, LUA_TSTRING); // key is string
1213 const char * key = luaL_checkstring(L, -2);
1214 if (!strcmp(key, "name")) {
1215 const char * name = luaL_checkstring(L, -1);
1216 str2zchar(limit->name, name, sizeof(limit->name));
1218 else if (!strcmp(key, "min")) {
1219 limit->min = luaL_checkinteger(L, -1)+1000;
1221 else if (!strcmp(key, "max")) {
1222 limit->max = luaL_checkinteger(L, -1)-1000;
1224 else if (!strcmp(key, "offset")) {
1225 limit->offset = luaL_checkinteger(L, -1);
1227 else if (!strcmp(key, "ppmCenter")) {
1228 limit->ppmCenter = luaL_checkinteger(L, -1);
1230 else if (!strcmp(key, "symetrical")) {
1231 limit->symetrical = luaL_checkinteger(L, -1);
1233 else if (!strcmp(key, "revert")) {
1234 limit->revert = luaL_checkinteger(L, -1);
1236 else if (!strcmp(key, "curve")) {
1237 limit->curve = luaL_checkinteger(L, -1) + 1;
1240 storageDirty(EE_MODEL);
1243 return 0;
1246 /*luadoc
1247 @function model.getGlobalVariable(index, flight_mode)
1249 Return current global variable value
1251 @notice a simple warning or notice
1253 @param index zero based global variable index, use 0 for GV1, 8 for GV9
1255 @param flight_mode Flight mode number (0 = FM0, 8 = FM8)
1257 @retval nil requested global variable does not exist
1259 @retval number current value of global variable
1261 Example:
1263 ```lua
1264 -- get GV3 (index = 2) from Flight mode 0 (FM0)
1265 val = model.getGlobalVariable(2, 0)
1268 static int luaModelGetGlobalVariable(lua_State *L)
1270 unsigned int idx = luaL_checkunsigned(L, 1);
1271 unsigned int phase = luaL_checkunsigned(L, 2);
1272 if (phase < MAX_FLIGHT_MODES && idx < MAX_GVARS)
1273 lua_pushinteger(L, g_model.flightModeData[phase].gvars[idx]);
1274 else
1275 lua_pushnil(L);
1276 return 1;
1279 /*luadoc
1280 @function model.setGlobalVariable(index, flight_mode, value)
1282 Sets current global variable value. See also model.getGlobalVariable()
1284 @param index zero based global variable index, use 0 for GV1, 8 for GV9
1286 @param flight_mode Flight mode number (0 = FM0, 8 = FM8)
1288 @param value new value for global variable. Permitted range is
1289 from -1024 to 1024.
1291 @notice Global variable can only store integer values,
1292 any floating point value is converted into integer value
1293 by truncating everything behind a floating point.
1295 static int luaModelSetGlobalVariable(lua_State *L)
1297 unsigned int idx = luaL_checkunsigned(L, 1);
1298 unsigned int phase = luaL_checkunsigned(L, 2);
1299 int value = luaL_checkinteger(L, 3);
1300 if (phase < MAX_FLIGHT_MODES && idx < MAX_GVARS && value >= -GVAR_MAX && value <= GVAR_MAX) {
1301 g_model.flightModeData[phase].gvars[idx] = value;
1302 storageDirty(EE_MODEL);
1304 return 0;
1307 const luaL_Reg modelLib[] = {
1308 { "getInfo", luaModelGetInfo },
1309 { "setInfo", luaModelSetInfo },
1310 { "getModule", luaModelGetModule },
1311 { "setModule", luaModelSetModule },
1312 { "getTimer", luaModelGetTimer },
1313 { "setTimer", luaModelSetTimer },
1314 { "resetTimer", luaModelResetTimer },
1315 { "getInputsCount", luaModelGetInputsCount },
1316 { "getInput", luaModelGetInput },
1317 { "insertInput", luaModelInsertInput },
1318 { "deleteInput", luaModelDeleteInput },
1319 { "deleteInputs", luaModelDeleteInputs },
1320 { "defaultInputs", luaModelDefaultInputs },
1321 { "getMixesCount", luaModelGetMixesCount },
1322 { "getMix", luaModelGetMix },
1323 { "insertMix", luaModelInsertMix },
1324 { "deleteMix", luaModelDeleteMix },
1325 { "deleteMixes", luaModelDeleteMixes },
1326 { "getLogicalSwitch", luaModelGetLogicalSwitch },
1327 { "setLogicalSwitch", luaModelSetLogicalSwitch },
1328 { "getCustomFunction", luaModelGetCustomFunction },
1329 { "setCustomFunction", luaModelSetCustomFunction },
1330 { "getCurve", luaModelGetCurve },
1331 { "setCurve", luaModelSetCurve },
1332 { "getOutput", luaModelGetOutput },
1333 { "setOutput", luaModelSetOutput },
1334 { "getGlobalVariable", luaModelGetGlobalVariable },
1335 { "setGlobalVariable", luaModelSetGlobalVariable },
1336 { NULL, NULL } /* sentinel */