mirror of
https://github.com/pascallanger/DIY-Multiprotocol-TX-Module.git
synced 2025-02-04 20:48:12 +00:00
62aa58d32d
1. NEW Black&White Radios new model setup. Now it can setup completely a plane from zero 2. Color version; simplified code for Tail mixes, and fix Taileron setup
788 lines
27 KiB
Lua
788 lines
27 KiB
Lua
--- #########################################################################
|
|
---- # #
|
|
---- # Copyright (C) OpenTX/EdgeTx #
|
|
-----# #
|
|
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
|
|
---- # #
|
|
---- # This program is free software; you can redistribute it and/or modify #
|
|
---- # it under the terms of the GNU General Public License version 2 as #
|
|
---- # published by the Free Software Foundation. #
|
|
---- # #
|
|
---- # This program is distributed in the hope that it will be useful #
|
|
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
|
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
|
---- # GNU General Public License for more details. #
|
|
---- # #
|
|
---- #########################################################################
|
|
|
|
------------------------------------------------------------------------------
|
|
-- This script library is a rewrite of the original DSM forward programming Lua
|
|
-- Script. The goal is to make it easier to understand, mantain, and to
|
|
-- separate the GUI from the DSM Forward programming engine/logic
|
|
-- in this way, GUIs can evolve independent. OpenTX Gui, EdgeTx GUI, Small Radios, etc.
|
|
|
|
-- Code is based on the code/work by: Pascal Langer (Author of the Multi-Module)
|
|
-- Rewrite/Enhancements By: Francisco Arzu
|
|
|
|
local Log, DEBUG_ON = ... -- Parameters
|
|
|
|
|
|
local MenuLib = { }
|
|
|
|
local PHASE = {
|
|
INIT = 0,
|
|
RX_VERSION = 1,
|
|
WAIT_CMD = 2,
|
|
MENU_TITLE = 3,
|
|
MENU_REQ_TX_INFO = 4,
|
|
MENU_LINES = 5,
|
|
MENU_VALUES = 6,
|
|
VALUE_CHANGING = 7,
|
|
VALUE_CHANGING_WAIT = 8,
|
|
VALUE_CHANGE_END = 9,
|
|
EXIT = 10,
|
|
EXIT_DONE = 11
|
|
}
|
|
|
|
local LINE_TYPE = {
|
|
MENU = 0x1C,
|
|
LIST_MENU = 0x0C, -- List: INC Change + Validate
|
|
LIST_MENU_NC = 0x6C, -- List: No Incremental Change
|
|
LIST_MENU_NC2 = 0x6D, -- List: No Incremental Change (Frame Rate Herz)
|
|
LIST_MENU_TOG = 0x4C, -- List: Incremental Change, sometimes bolean/Toggle menu (if only 2 values)
|
|
LIST_MENU_ORI = 0xCC, -- List: Incremental Change, Orientation Heli
|
|
|
|
VALUE_NUM_I8_NC = 0x60, -- 8 bit number, no incremental change
|
|
VALUE_PERCENT = 0xC0, -- 8 bit number, Signed, percent
|
|
VALUE_DEGREES = 0xE0, -- 8 bit number, Signed, Degress
|
|
VALUE_NUM_I8 = 0x40, -- 8 bit number
|
|
VALUE_NUM_I16 = 0x41, -- 16 Bit number
|
|
VALUE_NUM_SI16 = 0xC1, -- 16 bit number, Signed
|
|
|
|
LT_EMPTY = 0x00
|
|
}
|
|
|
|
-- Bug in Lua compiler, confusing with global BOLD and RIGHT
|
|
local DISP_ATTR = {
|
|
_BOLD = 0x01, _RIGHT=0x02, _CENTER=0x04, PERCENT = 0x10, DEGREES=0x20, FORCED_MENU = 0x40
|
|
}
|
|
|
|
--RX IDs--
|
|
local RX = {
|
|
AR636B = 0x0001,
|
|
SPM4651T = 0x0014,
|
|
AR637T = 0x0015,
|
|
AR637TA = 0x0016,
|
|
FC6250HX = 0x0018,
|
|
AR630 = 0x0019,
|
|
AR8360T = 0x001A,
|
|
AR10360T = 0x001C,
|
|
AR631 = 0x001E
|
|
}
|
|
|
|
local DSM_Context = {
|
|
Phase = PHASE.INIT,
|
|
Menu = { MenuId = 0, Text = "", TextId = 0, PrevId = 0, NextId = 0, BackId = 0 },
|
|
MenuLines = {},
|
|
RX = { Id=0, Name = "", Version = "" },
|
|
Refresh_Display = true,
|
|
SendDataToRX = 1,
|
|
|
|
SelLine = 0, -- Current Selected Line
|
|
EditLine = nil, -- Current Editing Line
|
|
CurLine = -1, -- Current Line Requested/Parsed via h message protocol
|
|
isReset = false -- false when starting from scracts, true when starting from Reset
|
|
}
|
|
|
|
function DSM_Context.isEditing() return DSM_Context.EditLine~=nil end
|
|
|
|
local MAX_MENU_LINES = 6
|
|
local BACK_BUTTON = -1 -- Tread it as a display line #-1
|
|
local NEXT_BUTTON = MAX_MENU_LINES + 1 -- Tread it as a display line #7
|
|
local PREV_BUTTON = MAX_MENU_LINES + 2 -- Tread it as a display line #7
|
|
|
|
-- Text Arrays for Display Text and Debuging
|
|
local PhaseText = {}
|
|
local LineTypeText = {}
|
|
|
|
local Text = {} -- Text for Menu and Menu Lines (Headers only)
|
|
local List_Text = {} -- Messages for List Options (values only)
|
|
local List_Text_Img = {} -- If the Text has Attached Images
|
|
local List_Values = {} -- Additiona restrictions on List Values when non contiguos (L_M1 lines has this problem)
|
|
local Flight_Mode = {[0]="Fligh Mode"}
|
|
local RxName = {}
|
|
|
|
local StartTime = 0
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
-- Get Elapsed Time since we started running the Script. Return a float in format: Seconds.Milliseconds
|
|
function MenuLib.getElapsedTime()
|
|
local t = getTime()
|
|
if (StartTime == 0) then StartTime = t end
|
|
|
|
return ((t - StartTime) * 10) / 1000
|
|
end
|
|
|
|
------------- Line Type helper functions ------------------------------------------------------------------
|
|
|
|
-- Check if the text are Flight modes, who will be treated different for Display
|
|
function MenuLib.isFlightModeLine(line)
|
|
return (line.TextId >= 0x8000 and line.TextId <= 0x8003)
|
|
end
|
|
|
|
function MenuLib.isSelectableLine(line) -- is the display line Selectable??
|
|
-- values who are not selectable
|
|
if (line.Type == 0) then return false end -- Empty Line
|
|
if (line.Type == LINE_TYPE.MENU and line.ValId == line.MenuId and bit32.band(line.TextAttr, DISP_ATTR.FORCED_MENU)==0) then return false end -- Menu that navigates to Itself?
|
|
if (line.Min==0 and line.Max==0 and line.Def==0) then return false end -- Values with no Range are only for display
|
|
if (line.Type == LINE_TYPE.VALUE_NUM_I8_NC and MenuLib.isFlightModeLine(line)) then return false end -- Flight mode is not Selectable
|
|
return true
|
|
end
|
|
|
|
function MenuLib.isEditableLine(line) -- is the display line editable??
|
|
-- values who are not editable
|
|
if (line.Type == 0 or line.Type == LINE_TYPE.MENU) then return false end -- Menus are not editable
|
|
if (line.Min==0 and line.Max==0 and line.Def==0) then return false end -- Values with no Range are only for display
|
|
if (line.Type == LINE_TYPE.VALUE_NUM_I8_NC and MenuLib.isFlightModeLine(line)) then return false end -- Flight mode is not Editable
|
|
-- any other is Editable
|
|
return true
|
|
end
|
|
|
|
function MenuLib.isListLine(line) -- is it a List of options??
|
|
if (line.Type == LINE_TYPE.LIST_MENU_NC or line.Type == LINE_TYPE.LIST_MENU or
|
|
line.Type == LINE_TYPE.LIST_MENU_TOG or line.Type == LINE_TYPE.LIST_MENU_NC2 or
|
|
line.Type == LINE_TYPE.LIST_MENU_ORI) then return true end
|
|
return false
|
|
end
|
|
|
|
function MenuLib.isPercentValueLineByMinMax(line)
|
|
return
|
|
(line.Min == 0 and line.Max == 100) or ( line.Min == -100 and line.Max == 100) or
|
|
( line.Min == 0 and line.Max == 150) or ( line.Min == -150 and line.Max == 150)
|
|
end
|
|
|
|
function MenuLib.isPercentValueLine(line) -- is it a Percent value??
|
|
if (line.Type == LINE_TYPE.VALUE_PERCENT) then return true end
|
|
return false
|
|
end
|
|
|
|
function MenuLib.isNumberValueLine(line) -- is it a number ??
|
|
if (MenuLib.isListLine(line) or line.Type == LINE_TYPE.MENU or line.Type == 0) then return false
|
|
else return true end
|
|
end
|
|
|
|
function MenuLib.isIncrementalValueUpdate(line)
|
|
if (line.Type == LINE_TYPE.LIST_MENU_NC or line.Type == LINE_TYPE.LIST_MENU_NC2 or
|
|
line.Type == LINE_TYPE.VALUE_NUM_I8_NC or line.Type == LINE_TYPE.VALUE_DEGREES) then return false end
|
|
return true
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
function MenuLib.Get_Text(index)
|
|
if (index >= 0x8000) then
|
|
return Flight_Mode[0]
|
|
end
|
|
|
|
local out = Text[index] -- Find in regular header first
|
|
if out== nil then
|
|
out = List_Text[index] -- Try list values, don't think is necesary, but just playing Safe
|
|
end
|
|
if out == nil then -- unknown...
|
|
out = "Unknown_" .. string.format("%X", index)
|
|
end
|
|
return out
|
|
end
|
|
|
|
function MenuLib.Get_List_Text(index)
|
|
local out = List_Text[index] -- Try to find the message in List_Text
|
|
if out == nil then
|
|
out = Text[index] -- Try list headers, don't think is necesary, but just playing Safe
|
|
end
|
|
if out == nil then -- unknown...
|
|
out = "UnknownLT_" .. string.format("%X", index)
|
|
end
|
|
return out
|
|
end
|
|
|
|
function MenuLib.Get_List_Text_Img(index)
|
|
local out = List_Text_Img[index]
|
|
return out
|
|
end
|
|
|
|
function MenuLib.Get_List_Values(index)
|
|
local out = List_Values[index]
|
|
return out
|
|
end
|
|
|
|
function MenuLib.Get_RxName(index)
|
|
local out = RxName[index]
|
|
return out or ("RX_" .. string.format("%X", index))
|
|
end
|
|
|
|
----------- Debugging 2-String functions -------------------------------------------------------------------
|
|
|
|
function MenuLib.phase2String(index)
|
|
local out = PhaseText[index]
|
|
return out or ("Phase_" .. string.format("%X", index))
|
|
end
|
|
|
|
function MenuLib.lineType2String(index)
|
|
local out = LineTypeText[index]
|
|
return out or ("LT_" .. string.format("%X", index or 0xFF))
|
|
end
|
|
|
|
function MenuLib.lineValue2String(l)
|
|
if (DEBUG_ON == 0) then
|
|
return ""
|
|
end
|
|
if (l ~= nil and l.Val ~= nil) then
|
|
local value = l.Val
|
|
if MenuLib.isListLine(l) then
|
|
value = value .. "|\"" .. MenuLib.Get_List_Text(l.Val + l.TextStart) .. "\""
|
|
else
|
|
value = value..(l.Format or "")
|
|
end
|
|
return value
|
|
end
|
|
return "nil"
|
|
end
|
|
|
|
function MenuLib.menu2String(m)
|
|
local txt = "Menu[]"
|
|
if (m ~= nil) then
|
|
txt = string.format("M[Id=0x%X P=0x%X N=0x%X B=0x%X Text=\"%s\"[0x%X]]",
|
|
m.MenuId, m.PrevId, m.NextId, m.BackId, m.Text, m.TextId)
|
|
end
|
|
return txt
|
|
end
|
|
|
|
function MenuLib.menuLine2String(l)
|
|
local txt = "Line[]"
|
|
if (l ~= nil) then
|
|
local value = ""
|
|
local range = ""
|
|
if l.Type~=LINE_TYPE.MENU then
|
|
value = "Val="..MenuLib.lineValue2String(l)
|
|
if MenuLib.isListLine(l) then
|
|
range = string.format("NL=(%s->%s,%s,S=%s) ",l.Min, l.Max, l.Def, l.TextStart )
|
|
range = range .. (l.MinMaxOrig or "")
|
|
else
|
|
range = string.format("[%s->%s,%s]",l.Min, l.Max, l.Def)
|
|
end
|
|
end
|
|
|
|
txt = string.format("L[#%s T=%s VId=0x%X Text=\"%s\"[0x%X] %s %s MId=0x%X A=0x%X]",
|
|
l.lineNum, MenuLib.lineType2String(l.Type), l.ValId,
|
|
l.Text, l.TextId,
|
|
value,
|
|
range,
|
|
l.MenuId,
|
|
l.TextAttr
|
|
)
|
|
end
|
|
return txt
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------------------------
|
|
-- Post Procssing Line from Raw values receive by RX or Simulation
|
|
|
|
function MenuLib.isDisplayAttr(attr, bit)
|
|
return (bit32.band(attr,bit)>0)
|
|
end
|
|
|
|
function MenuLib.ExtractDisplayAttr(text1, attr)
|
|
local text = text1, pos;
|
|
|
|
for i=1,2 do
|
|
text, pos = string.gsub(text, "/c$", "")
|
|
if (pos>0) then -- CENTER
|
|
attr = bit32.bor(attr, DISP_ATTR._CENTER)
|
|
end
|
|
|
|
text, pos = string.gsub(text, "/r$", "")
|
|
if (pos>0) then -- RIGHT
|
|
attr = bit32.bor(attr, DISP_ATTR._RIGHT)
|
|
end
|
|
|
|
text, pos = string.gsub(text, "/p$", "")
|
|
if (pos>0) then -- Percent TEXT
|
|
attr = bit32.bor(attr, DISP_ATTR.PERCENT)
|
|
end
|
|
|
|
text, pos = string.gsub(text, "/b$", "")
|
|
if (pos>0) then -- BOLD TEXT
|
|
attr = bit32.bor(attr, DISP_ATTR._BOLD)
|
|
end
|
|
|
|
text, pos = string.gsub(text, "/m$", "")
|
|
if (pos>0) then -- FORCED MENU Button
|
|
attr = bit32.bor(attr, DISP_ATTR.FORCED_MENU)
|
|
end
|
|
end
|
|
|
|
return text, attr
|
|
end
|
|
|
|
function MenuLib.MenuPostProcessing(menu)
|
|
menu.Text, menu.TextAttr = MenuLib.ExtractDisplayAttr(menu.Text,menu.TextAttr or 0)
|
|
end
|
|
|
|
function MenuLib.MenuLinePostProcessing(line)
|
|
if (line.Text==nil) then
|
|
line.Text = MenuLib.Get_Text(line.TextId) -- Get Textual Line headeing text
|
|
end
|
|
|
|
-- Text formatting options
|
|
line.Text, line.TextAttr = MenuLib.ExtractDisplayAttr(line.Text,line.TextAttr or 0)
|
|
|
|
if line.Type == LINE_TYPE.MENU then
|
|
-- nothing to do on menu entries
|
|
line.Val=nil
|
|
elseif MenuLib.isListLine(line) then
|
|
-- Original Range for Debugging
|
|
line.MinMaxOrig = "[" .. line.Min .. "->" .. line.Max .. "," .. line.Def .. "]"
|
|
|
|
-- Normalize Min/Max to be relative to Zero
|
|
line.TextStart = line.Min
|
|
line.Def = line.Def - line.Min -- normalize default value
|
|
line.Max = line.Max - line.Min -- normalize max index
|
|
line.Min = 0 -- min index
|
|
else -- default to numerical value
|
|
if MenuLib.isPercentValueLine(line) or MenuLib.isPercentValueLineByMinMax(line) then
|
|
-- either explicit Percent or NO-Change value, but range is %Percent
|
|
line.Format ="%"
|
|
line.TextAttr = bit32.bor(line.TextAttr,DISP_ATTR.PERCENT)
|
|
elseif (line.Type == LINE_TYPE.VALUE_DEGREES) then
|
|
line.Format ="o"
|
|
line.TextAttr = bit32.bor(line.TextAttr,DISP_ATTR.DEGREES)
|
|
end
|
|
end
|
|
|
|
line.MinMaxDebug = MenuLib.lineType2String(line.Type).." "..(line.MinMaxOrig or "")
|
|
end
|
|
|
|
|
|
function MenuLib.ChangePhase(newPhase)
|
|
DSM_Context.Phase = newPhase
|
|
DSM_Context.SendDataToRX = 1
|
|
end
|
|
|
|
function MenuLib.Value_Add(line, inc)
|
|
if (DEBUG_ON) then Log.LOG_write("%3.3f %s: DSM_Value_Add(%s,%s)\n",
|
|
MenuLib.getElapsedTime(), MenuLib.phase2String(DSM_Context.Phase), inc, MenuLib.menuLine2String(line)) end
|
|
|
|
local skipIncrement = false
|
|
local values = nil
|
|
local origVal = line.Val
|
|
|
|
-- Use local validation for LIST_MENU1 when the range is wide open
|
|
-- Also use if for some LIST_MENU0 that the Range seems incorrect
|
|
if (MenuLib.isListLine(line)) then -- and line.Type==LINE_TYPE.LIST_MENU1 and line.Min==0 and line.Max==244) then
|
|
values = MenuLib.Get_List_Values(line.TextId)
|
|
end
|
|
|
|
|
|
if (values~=nil) then -- Inc/Dec based on a list of predefined Values Local to Script (values not contiguous),
|
|
-- locate current value in values array
|
|
-- Values are Zero normalized to the Start of the List (line.TextStart)
|
|
for i = 1, #values do
|
|
if ((values[i]-line.TextStart)==origVal) then
|
|
skipIncrement = true
|
|
if (inc==-1 and i > 1) then -- PREV
|
|
line.Val = values[i-1]-line.TextStart
|
|
elseif (inc==1 and i < #values) then -- NEXT
|
|
line.Val = values[i+1]-line.TextStart
|
|
end
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if not skipIncrement then
|
|
-- Do it Sequentially
|
|
line.Val = line.Val + inc
|
|
|
|
if line.Val > line.Max then
|
|
line.Val = line.Max
|
|
elseif line.Val < line.Min then
|
|
line.Val = line.Min
|
|
end
|
|
end
|
|
|
|
if (origVal~=line.Val and MenuLib.isIncrementalValueUpdate(line)) then
|
|
-- Update RX value on every change
|
|
MenuLib.ChangePhase(PHASE.VALUE_CHANGING)
|
|
end
|
|
end
|
|
|
|
function MenuLib.Value_Default(line)
|
|
local origVal = line.Val
|
|
if (DEBUG_ON) then Log.LOG_write("%3.3f %s: DSM_Value_Default(%s)\n",
|
|
MenuLib.getElapsedTime(), MenuLib.phase2String(DSM_Context.Phase), MenuLib.menuLine2String(line)) end
|
|
|
|
line.Val = line.Def
|
|
if (origVal~=line.Val and MenuLib.isIncrementalValueUpdate(line)) then
|
|
-- Update RX value on every change
|
|
MenuLib.ChangePhase(PHASE.VALUE_CHANGING)
|
|
end
|
|
end
|
|
|
|
function MenuLib.Value_Write_Validate(line)
|
|
if (DEBUG_ON) then Log.LOG_write("%3.3f %s: DSM_Value_Write_Validate(%s)\n",
|
|
MenuLib.getElapsedTime(), MenuLib.phase2String(DSM_Context.Phase), MenuLib.menuLine2String(line)) end
|
|
|
|
MenuLib.ChangePhase(PHASE.VALUE_CHANGE_END) -- Update + Validate value in RX
|
|
DSM_Context.EditLine = nil -- Exit Edit Mode (By clearing the line editing)
|
|
end
|
|
|
|
function MenuLib.GotoMenu(menuId, lastSelectedLine)
|
|
if (DEBUG_ON) then Log.LOG_write("%3.3f %s: DSM_GotoMenu(0x%X,LastSelectedLine=%d)\n",
|
|
MenuLib.getElapsedTime(), MenuLib.phase2String(DSM_Context.Phase), menuId, lastSelectedLine) end
|
|
|
|
DSM_Context.Menu.MenuId = menuId
|
|
DSM_Context.SelLine = lastSelectedLine
|
|
-- Request to load the menu Again
|
|
MenuLib.ChangePhase(PHASE.MENU_TITLE)
|
|
end
|
|
|
|
function MenuLib.MoveSelectionLine(dir)
|
|
local ctx = DSM_Context
|
|
local menu = ctx.Menu
|
|
local menuLines = ctx.MenuLines
|
|
|
|
if (dir == 1) then -- NEXT
|
|
if ctx.SelLine <= MAX_MENU_LINES then
|
|
local num = ctx.SelLine
|
|
for i = ctx.SelLine + 1, MAX_MENU_LINES, 1 do
|
|
if MenuLib.isSelectableLine(menuLines[i]) then
|
|
ctx.SelLine = i
|
|
break
|
|
end
|
|
end
|
|
|
|
if num == ctx.SelLine then
|
|
if menu.NextId ~= 0 then -- Next
|
|
ctx.SelLine = NEXT_BUTTON
|
|
elseif menu.PrevId ~= 0 then -- Prev
|
|
ctx.SelLine = PREV_BUTTON
|
|
end
|
|
end
|
|
elseif menu.PrevId ~= 0 then -- Prev
|
|
ctx.SelLine = PREV_BUTTON
|
|
end
|
|
return
|
|
end
|
|
|
|
if (dir == -1) then -- PREV
|
|
if ctx.SelLine == PREV_BUTTON and menu.NextId ~= 0 then
|
|
ctx.SelLine = NEXT_BUTTON
|
|
elseif ctx.SelLine > 0 then
|
|
if ctx.SelLine > MAX_MENU_LINES then
|
|
ctx.SelLine = NEXT_BUTTON
|
|
end
|
|
local num = ctx.SelLine
|
|
for i = ctx.SelLine - 1, 0, -1 do
|
|
if MenuLib.isSelectableLine(menuLines[i]) then
|
|
ctx.SelLine = i
|
|
break
|
|
end
|
|
end
|
|
if num == ctx.SelLine then -- can't find previous selectable line, then SELECT Back
|
|
if (menu.BackId ~= 0) then ctx.SelLine = BACK_BUTTON end
|
|
end
|
|
else
|
|
if (menu.BackId ~= 0) then ctx.SelLine = BACK_BUTTON end -- Back
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- Clear each line of the menu
|
|
function MenuLib.clearMenuLines()
|
|
local ctx = DSM_Context
|
|
for i = 0, MAX_MENU_LINES do -- clear menu
|
|
ctx.MenuLines[i] = { MenuId = 0, lineNum = 0, Type = 0, Text = "", TextId = 0, ValId = 0, Min=0, Max=0, Def=0, TextStart=0, Val=nil }
|
|
end
|
|
end
|
|
|
|
-- Post processing needed for each menu
|
|
function MenuLib.PostProcessMenu()
|
|
local ctx = DSM_Context
|
|
|
|
if (ctx.Menu.Text==nil) then
|
|
ctx.Menu.Text = MenuLib.Get_Text(ctx.Menu.TextId)
|
|
MenuLib.MenuPostProcessing (ctx.Menu)
|
|
end
|
|
|
|
--if (DEBUG_ON) then Log.LOG_write("SIM RESPONSE Menu: %s\n", MenuLib.menu2String(ctx.Menu)) end
|
|
|
|
for i = 0, MenuLib.MAX_MENU_LINES do -- clear menu
|
|
local line = ctx.MenuLines[i]
|
|
if (line.Type~=0) then
|
|
line.MenuId = ctx.Menu.MenuId
|
|
line.lineNum = i
|
|
MenuLib.MenuLinePostProcessing(line) -- Do the same post processing as if they come from the RX
|
|
--if (DEBUG_ON) then Log.LOG_write("SIM RESPONSE MenuLine: %s\n", MenuLib.menuLine2String(line)) end
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
function MenuLib.GetFlightModeValue(line)
|
|
local ret = line.Text.." "
|
|
local val = line.Val
|
|
|
|
if (val==nil) then return ret.."--" end
|
|
|
|
-- Adjust the displayed value for Flight mode line as needed
|
|
if (DSM_Context.RX.Id == RX.FC6250HX) then
|
|
-- Helicopter Flights modes
|
|
if (val==0) then ret = ret .. "1 / HOLD"
|
|
elseif (val==1) then ret = ret .. "2 / Normal"
|
|
elseif (val==2) then ret = ret .. "3 / Stunt 1"
|
|
elseif (val==3) then ret = ret .. "4 / Stunt 2"
|
|
elseif (val==4) then ret = ret .. "5 / Panic"
|
|
else
|
|
ret = ret .. " " .. (val + 1)
|
|
end
|
|
else
|
|
-- No adjustment needed
|
|
if (val==190) then
|
|
ret=ret.."Err:Out of Range"
|
|
else
|
|
ret=ret..(val + 1)
|
|
end
|
|
|
|
end
|
|
return ret
|
|
end
|
|
|
|
function MenuLib.Init()
|
|
print("MenuLib.Init()")
|
|
-- Phase Names
|
|
PhaseText[PHASE.INIT] = "INIT"
|
|
PhaseText[PHASE.RX_VERSION] = "RX_VERSION"
|
|
PhaseText[PHASE.WAIT_CMD] = "WAIT_CMD"
|
|
PhaseText[PHASE.MENU_TITLE] = "MENU_TITLE"
|
|
PhaseText[PHASE.MENU_REQ_TX_INFO] = "MENU_REQ_TX_INFO"
|
|
PhaseText[PHASE.MENU_LINES] = "MENU_LINES"
|
|
PhaseText[PHASE.MENU_VALUES] = "MENU_VALUES"
|
|
PhaseText[PHASE.VALUE_CHANGING] = "VALUE_CHANGING"
|
|
PhaseText[PHASE.VALUE_CHANGING_WAIT] = "VALUE_EDITING"
|
|
PhaseText[PHASE.VALUE_CHANGE_END] = "VALUE_CHANGE_END"
|
|
PhaseText[PHASE.EXIT] = "EXIT"
|
|
PhaseText[PHASE.EXIT_DONE] = "EXIT_DONE"
|
|
|
|
|
|
-- Line Types
|
|
LineTypeText[LINE_TYPE.MENU] = "M"
|
|
LineTypeText[LINE_TYPE.LIST_MENU_NC] = "LM_nc"
|
|
LineTypeText[LINE_TYPE.LIST_MENU] = "LM"
|
|
LineTypeText[LINE_TYPE.LIST_MENU_TOG] = "LM_tog"
|
|
LineTypeText[LINE_TYPE.LIST_MENU_NC2] = "LM_nc2"
|
|
LineTypeText[LINE_TYPE.LIST_MENU_ORI] = "LM_ori"
|
|
LineTypeText[LINE_TYPE.VALUE_NUM_I8_NC] = "V_nc"
|
|
LineTypeText[LINE_TYPE.VALUE_PERCENT] = "V_%"
|
|
LineTypeText[LINE_TYPE.VALUE_DEGREES] = "V_de"
|
|
LineTypeText[LINE_TYPE.VALUE_NUM_I8] = "V_i8"
|
|
LineTypeText[LINE_TYPE.VALUE_NUM_I16] = "V_i16"
|
|
LineTypeText[LINE_TYPE.VALUE_NUM_SI16] = "V_s16"
|
|
LineTypeText[LINE_TYPE.LT_EMPTY] = "Z"
|
|
|
|
DSM_Context.Phase = PHASE.RX_VERSION
|
|
end
|
|
|
|
function MenuLib.clearAllText()
|
|
local function clearTable(t)
|
|
for i, v in ipairs(t) do t[i] = nil end
|
|
end
|
|
|
|
clearTable(Text)
|
|
clearTable(List_Text)
|
|
clearTable(List_Text_Img)
|
|
clearTable(List_Values)
|
|
end
|
|
|
|
function MenuLib.LoadTextFromFile(fileName, mem)
|
|
local function rtrim(s)
|
|
local n = string.len(s)
|
|
while n > 0 and string.find(s, "^%s", n) do n = n - 1 end
|
|
return string.sub(s, 1, n)
|
|
end
|
|
|
|
--print(string.format("Loading messages from [%s]",fileName))
|
|
local dataFile = io.open(fileName, "r") -- read File
|
|
-- cannot read file???
|
|
assert(dataFile, "Cannot load Message file:" .. fileName)
|
|
|
|
local data = io.read(dataFile, mem * 1024) -- read up to 10k characters (newline char also counts!)
|
|
io.close(dataFile)
|
|
|
|
collectgarbage("collect")
|
|
|
|
local lineNo = 0
|
|
for line in string.gmatch(data, "[^\r\n]+") do
|
|
lineNo = lineNo + 1
|
|
--print(string.format("Line [%d]: %s",lineNo,line))
|
|
|
|
-- Remove Comments
|
|
local s = string.find(line, "--", 1, true)
|
|
if (s ~= nil) then
|
|
line = string.sub(line, 1, s - 1)
|
|
end
|
|
|
|
line = rtrim(line)
|
|
|
|
if (string.len(line) > 0) then
|
|
local a, b, c = string.match(line, "%s*(%a*)%s*|%s*(%w*)%s*|(.*)%s*")
|
|
--print(string.format("[%s] [%s] [%s]",a,b,c))
|
|
if (a ~= nil) then
|
|
local index = tonumber(b)
|
|
|
|
if (index == nil) then
|
|
assert(false, string.format("%s:%d: Invalid Hex num [%s]", fileName, lineNo, b))
|
|
elseif (a == "T") then
|
|
Text[index] = c
|
|
elseif (a == "LT") then
|
|
List_Text[index] = c
|
|
elseif (a == "LI") then
|
|
List_Text_Img[index] = c
|
|
elseif (a == "FM") then
|
|
Flight_Mode[0] = c
|
|
elseif (a == "RX") then
|
|
RxName[index] = c
|
|
else
|
|
assert(false, string.format("%s:%d: Invalid Line Type [%s]", fileName, lineNo, a))
|
|
end
|
|
end
|
|
end
|
|
if (lineNo % 50 == 0) then
|
|
collectgarbage("collect")
|
|
end
|
|
end -- For
|
|
|
|
--print(string.format("Loaded [%d] messages",lineNo))
|
|
data = nil
|
|
end
|
|
|
|
function MenuLib.INC_LoadTextFromFile(fileName, FileState)
|
|
-----------------------
|
|
local function rtrim(s)
|
|
local n = string.len(s)
|
|
while n > 0 and string.find(s, "^%s", n) do n = n - 1 end
|
|
return string.sub(s, 1, n)
|
|
end
|
|
|
|
local function GetTextInfoFromFile(pos)
|
|
local dataFile = io.open(fileName, "r")
|
|
io.seek(dataFile,pos)
|
|
local buff = io.read(dataFile, 100)
|
|
io.close(dataFile)
|
|
|
|
local line=""
|
|
local index=""
|
|
local type=""
|
|
|
|
local pipe=0
|
|
local comment=0
|
|
local newPos = pos
|
|
|
|
-- Parse the line:
|
|
-- Format: TT|0x999|Text -- Comment
|
|
for i=1,#buff do
|
|
newPos=newPos+1
|
|
local ch = string.sub(buff,i,i)
|
|
|
|
if (pipe < 2 and ch=="|") then pipe=pipe+1 -- Count pipes pos (Type | Index | .....)
|
|
elseif (ch=="\r") then -- Ignore CR
|
|
elseif (ch=="\n") then break -- LF, end of line
|
|
elseif (ch=="-") then -- March comments
|
|
comment=comment+1
|
|
if (comment==2) then pipe=6 end -- Comment part of line
|
|
else
|
|
-- regular char
|
|
comment=0
|
|
if (pipe==0) then type=type..ch -- in TT (Type)
|
|
elseif (pipe==1) then index=index..ch -- in Index
|
|
elseif (pipe<6) then line=line..ch end -- in Text
|
|
end -- Regular char
|
|
end -- For
|
|
|
|
return type, index, rtrim(line), newPos
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
|
|
if (FileState.state==nil) then -- Initial State
|
|
FileState.state=1
|
|
FileState.lineNo=0
|
|
FileState.filePos=0
|
|
end
|
|
|
|
if FileState.state==1 then
|
|
for l=1,10 do -- do 10 lines at a time
|
|
local type, sIndex, text
|
|
local lineStart = FileState.filePos
|
|
|
|
type, sIndex, text, FileState.filePos = GetTextInfoFromFile(FileState.filePos)
|
|
|
|
--print(string.format("T=%s, I=%s, T=%s LS=%d, FP=%d",type,sIndex,text,lineStart, FileState.filePos))
|
|
|
|
if (lineStart==FileState.filePos) then -- EOF
|
|
FileState.state=2 --EOF State
|
|
return 1
|
|
end
|
|
FileState.lineNo = FileState.lineNo + 1
|
|
|
|
type = rtrim(type)
|
|
|
|
if (string.len(type) > 0 and string.len(sIndex) > 0) then
|
|
local index = tonumber(sIndex)
|
|
|
|
if (index == nil) then
|
|
assert(false, string.format("%s:%d: Invalid Hex num [%s]", fileName, FileState.lineNo, sIndex))
|
|
elseif (type == "T") then
|
|
Text[index] = text
|
|
elseif (type == "LT") then
|
|
List_Text[index] = text
|
|
elseif (type == "LI") then
|
|
List_Text_Img[index] = text
|
|
elseif (type == "FM") then
|
|
Flight_Mode[0] = text
|
|
elseif (type == "RX") then
|
|
RxName[index] = text
|
|
else
|
|
assert(false, string.format("%s:%d: Invalid Line Type [%s]", fileName, FileState.lineNo, type))
|
|
end
|
|
end
|
|
end -- for
|
|
end -- if
|
|
|
|
return 0
|
|
end
|
|
|
|
|
|
-- Export some Constants and Variables
|
|
MenuLib.PHASE = PHASE
|
|
MenuLib.LINE_TYPE = LINE_TYPE
|
|
MenuLib.DISP_ATTR = DISP_ATTR
|
|
MenuLib.RX = RX
|
|
|
|
MenuLib.MAX_MENU_LINES = MAX_MENU_LINES
|
|
MenuLib.BACK_BUTTON = BACK_BUTTON
|
|
MenuLib.NEXT_BUTTON = NEXT_BUTTON
|
|
MenuLib.PREV_BUTTON = PREV_BUTTON
|
|
|
|
MenuLib.DSM_Context = DSM_Context
|
|
|
|
MenuLib.Text = Text
|
|
MenuLib.List_Text = List_Text
|
|
MenuLib.List_Text_Img = List_Text_Img
|
|
MenuLib.List_Values = List_Values
|
|
|
|
MenuLib.LOG_open = Log.LOG_open
|
|
MenuLib.LOG_write = Log.LOG_write
|
|
MenuLib.LOG_Close = Log.LOG_close
|
|
|
|
|
|
return MenuLib |