Added first two Lua script conversions; autopilot airspeed and ATC visibility
Updated DelphiLua Added scripting documentation
This commit is contained in:
parent
6dbea6f211
commit
897ba9310e
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@ __history
|
||||
*.exe
|
||||
*.identcache
|
||||
Docs/_build/
|
||||
*.sublime-workspace
|
||||
|
BIN
Docs/autoPilotAirspeedStates.png
Normal file
BIN
Docs/autoPilotAirspeedStates.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
@ -7,3 +7,4 @@ G940LEDControl documentation
|
||||
|
||||
introduction
|
||||
scripting
|
||||
scriptingreference
|
@ -3,7 +3,7 @@ Introduction
|
||||
|
||||
G940LEDControl allows you to bind functions to the throttle's buttons which control the LEDs. Each function may contains several states, each of which can be configured for a certain colour and/of flashing pattern.
|
||||
|
||||
Since version 2.0 all functions are implemented using Lua scripts. This means it is fairly easy to create customized versions of the standard functions, or even add a completely new function. For more information, see the page on :doc:`scripting`.
|
||||
Since version 2.0 all functions are implemented using Lua scripts. This means it is fairly easy to create customized versions of the standard functions, or even add a completely new function. For more information, see the page on :doc:`scripting` or dive straight into the :doc:`scriptingreference`.
|
||||
|
||||
Source code
|
||||
-----------
|
||||
@ -12,8 +12,11 @@ Since version 2.0, G940LEDControl is released as open-source under the GNU Gener
|
||||
|
||||
G940LEDControl is compiled using Delphi XE2. The following additional libraries are required:
|
||||
|
||||
* `OmniThreadLibrary <http://www.omnithreadlibrary.com/>`_ (1.0.4)
|
||||
* `VirtualTreeView <http://www.jam-software.com/virtual-treeview/>`_ (5.3.0)
|
||||
* `X2Log <https://git.x2software.net/delphi/x2log>`_
|
||||
* `X2Utils <https://git.x2software.net/delphi/x2utils>`_
|
||||
* ToDo: any more?
|
||||
|
||||
A copy of `DelphiLua <https://git.x2software.net/delphi/delphilua>`_ is already included in the G940LEDControl repository.
|
||||
Newer versions Delphi and/or of the libraries might work as well, though have not been tested yet.
|
||||
|
||||
A copy of `DelphiLua <https://git.x2software.net/delphi/delphilua>`_ is included in the G940LEDControl repository.
|
@ -1,7 +1,117 @@
|
||||
Scripting
|
||||
=========
|
||||
|
||||
All functionality introduced by G940LEDControl is described in the :doc:`scriptingreference`. For more information about Lua in general, please refer to the `Lua 5.2 Reference Manual <https://www.lua.org/manual/5.2/>`_.
|
||||
|
||||
This guide will walk through how Lua scripting works in G940LEDControl. To avoid confusion when talking about functions in this context they will be referred to as 'button functions' and 'Lua functions'.
|
||||
|
||||
|
||||
Script locations
|
||||
----------------
|
||||
The default scripts included with G940LEDControl can be found in the folder selected during installation, for example "C:\\Program Files (x86)\\G940 LED Control". In there you will find a Scripts\\FSX folder containing the Lua files.
|
||||
|
||||
In addition scripts are loaded from your user data path. This folder is automatically created by G940LEDControl after you have changed any button configuration or settings. To open the folder, type or paste "%APPDATA%\\G940LEDControl" into a Windows Explorer address bar and press Enter. Inside you can create the same Scripts\\FSX folder structure. Scripts in this folder will not be overwritten when installing a new version.
|
||||
|
||||
|
||||
Anatomy of a button function
|
||||
----------------------------
|
||||
Let's take the Autopilot airspeed button function as an example, which at the time of writing looks like this:
|
||||
|
||||
::
|
||||
|
||||
-- this could be a great Lua example!
|
||||
local strings = require './lib/strings'
|
||||
|
||||
RegisterFunction(
|
||||
{
|
||||
uid = 'autoPilotAirspeed',
|
||||
category = strings.Category.FSX.AutoPilot,
|
||||
displayName = 'Autopilot airspeed',
|
||||
states = {
|
||||
on = { displayName = 'On', default = LEDColor.Green },
|
||||
off = { displayName = 'Off', default = LEDColor.Red },
|
||||
notAvailable = { displayName = 'Not available', default = LEDColor.Off }
|
||||
}
|
||||
},
|
||||
function(context)
|
||||
SetState(context, 'notAvailable')
|
||||
|
||||
OnSimConnect(context,
|
||||
{
|
||||
autoPilotAvailable = { variable = 'AUTOPILOT AVAILABLE', type = SimConnectDataType.Bool },
|
||||
autoPilotAirspeed = { variable = 'AUTOPILOT AIRSPEED HOLD', type = SimConnectDataType.Bool }
|
||||
},
|
||||
function(context, data)
|
||||
if data.autoPilotAvailable then
|
||||
if data.autoPilotAirspeed then
|
||||
SetState(context, 'on')
|
||||
else
|
||||
SetState(context, 'off')
|
||||
end
|
||||
else
|
||||
SetState(context, 'notAvailable')
|
||||
end
|
||||
end)
|
||||
end
|
||||
)
|
||||
|
||||
Using anonymous functions like this to implement the various callbacks results in compact code, which can arguably be more difficult to read. For clarity let's expand it first. The following example works exactly the same:
|
||||
|
||||
::
|
||||
|
||||
local strings = require './lib/strings'
|
||||
|
||||
local function variablesChanged(context, data)
|
||||
if data.autoPilotAvailable then
|
||||
if data.autoPilotAirspeed then
|
||||
SetState(context, 'on')
|
||||
else
|
||||
SetState(context, 'off')
|
||||
end
|
||||
else
|
||||
SetState(context, 'notAvailable')
|
||||
end
|
||||
end
|
||||
|
||||
local function setup(context)
|
||||
SetState(context, 'notAvailable')
|
||||
|
||||
OnSimConnect(context,
|
||||
{
|
||||
autoPilotAvailable = { variable = 'AUTOPILOT AVAILABLE', type = SimConnectDataType.Bool },
|
||||
autoPilotAirspeed = { variable = 'AUTOPILOT AIRSPEED HOLD', type = SimConnectDataType.Bool }
|
||||
},
|
||||
variablesChanged)
|
||||
end
|
||||
|
||||
|
||||
RegisterFunction(
|
||||
{
|
||||
uid = 'autoPilotAirspeed',
|
||||
category = strings.Category.FSX.AutoPilot,
|
||||
displayName = 'Autopilot airspeed',
|
||||
states = {
|
||||
on = { displayName = 'On', default = LEDColor.Green },
|
||||
off = { displayName = 'Off', default = LEDColor.Red },
|
||||
notAvailable = { displayName = 'Not available', default = LEDColor.Off }
|
||||
}
|
||||
},
|
||||
setup)
|
||||
|
||||
|
||||
So what's happening? When the script is loaded it is automatically run. At this time you should call :ref:`ref-registerfunction` for each button function you want to be visible in G940LEDControl. :ref:`ref-registerfunction` accepts two parameters: a table which describes the button function you want to add, and a Lua function to be called when the button function is assigned to a button.
|
||||
|
||||
Every button function must have a unique 'uid'. It is used to save and load profiles and should therefore not be changed once in use. The category and displayName are only used in the main and button function selection screens and can be freely changed.
|
||||
|
||||
A button function must also have one or more states. Each state has a key which, like the 'uid', is used to save and load profiles and should also not be changed once in use. A default :ref:`LED color <ref-ledcolor>` can also be set, which sets the initial value in the selection screen when assigning it to a button:
|
||||
|
||||
.. image:: autoPilotAirspeedStates.png
|
||||
|
||||
|
||||
Setup function
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
As soon as the button function is attached to one of the buttons the setup function passed to :ref:`ref-registerfunction` is called. It receives a 'context' parameter, the contents of which are not useable by the script directly, but which you need to pass along to for example :ref:`ref-setstate` later on so that it knows which button function's state needs to be changed.
|
||||
|
||||
In the above example the first thing we do is to set the default state using :ref:`ref-setstate`. The second parameter is a string containing the key of one of the states as defined in the :ref:`ref-registerfunction` call.
|
||||
|
||||
After that you will normally call one of the built-in Lua functions to be notified of certain events. At the time of writing you can either call :ref:`ref-ontimer` to perform checks on a regular interval or, more common in the case of FSX, :ref:`ref-onsimconnect` to be notified when one or more of the `simulation variables <https://msdn.microsoft.com/en-us/library/cc526981.aspx>`_ change.
|
323
Docs/scriptingreference.rst
Normal file
323
Docs/scriptingreference.rst
Normal file
@ -0,0 +1,323 @@
|
||||
Scripting reference
|
||||
===================
|
||||
|
||||
G940LEDControl uses Lua 5.2. Please refer to the `Lua 5.2 Reference Manual <https://www.lua.org/manual/5.2/>`_ for more information.
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Global functions
|
||||
----------------
|
||||
|
||||
.. _ref-log:
|
||||
|
||||
Log functions
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
Log.Verbose(msg, ...)
|
||||
Log.Info(msg, ...)
|
||||
Log.Warning(msg, ...)
|
||||
Log.Error(msg, ...)
|
||||
|
||||
Writes a message to the application log. If you pass more than one parameter, they will be concatenated in the log message separated by spaces. The parameters do not need to be strings, other simple types will be converted and tables will be output as '{ key = value, ... }'.
|
||||
|
||||
The application log can be opened on the Configuration tab of the main screen.
|
||||
|
||||
.. _ref-registerfunction:
|
||||
|
||||
RegisterFunction
|
||||
~~~~~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
RegisterFunction(info, setupCallback)
|
||||
|
||||
Registers a button function.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| **info**: table
|
||||
| A Lua table describing the function. The following keys are recognized:
|
||||
|
|
||||
| **uid**: string
|
||||
| *Required.* A unique ID for this function. Used to save and load profiles.
|
||||
|
|
||||
| **category**: string
|
||||
| The category under which this function is grouped in the button function selection screen.
|
||||
|
|
||||
| **displayName**: string
|
||||
| The name of the function which is shown in the main screen and button function selection screen.
|
||||
|
|
||||
| **states**: table
|
||||
| A table of states supported by this function.
|
||||
| Each state has it's own unique key and a table describing the state. The following keys are recognized for the table:
|
||||
|
|
||||
| **displayName**: string
|
||||
| The name of the state which is shown in the button function selection screen.
|
||||
|
|
||||
| **default**: string
|
||||
| The default color and/or animation assigned to the state when it is first selected. See :ref:`ref-ledcolor` for a list of valid values.
|
||||
|
|
||||
| **setupCallback**: function
|
||||
| A Lua function which is called when the button function is configured. Please note that if a button function is linked to multiple G940 throttle buttons, setupCallback is called multiple times, so be careful with variables which are outside of the setupCallback's scope (global or script-local)!
|
||||
|
|
||||
| setupCallback is passed a single parameter 'context'.
|
||||
|
|
||||
|
||||
**Example**
|
||||
::
|
||||
|
||||
RegisterFunction(
|
||||
{
|
||||
uid = 'autoPilotAirspeed',
|
||||
category = strings.Category.FSX.AutoPilot,
|
||||
displayName = 'Autopilot airspeed',
|
||||
states = {
|
||||
on = { displayName = 'On', default = LEDColor.Green },
|
||||
off = { displayName = 'Off', default = LEDColor.Red },
|
||||
notAvailable = { displayName = 'Not available', default = LEDColor.Off }
|
||||
}
|
||||
},
|
||||
function(context)
|
||||
-- implementation of setupCallback
|
||||
end)
|
||||
|
||||
.. _ref-setstate:
|
||||
|
||||
SetState
|
||||
~~~~~~~~
|
||||
::
|
||||
|
||||
SetState(context, newState)
|
||||
|
||||
Sets the current state of a button function.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| **context**
|
||||
| The context parameter as passed to setupCallback which determines the button function to be updated.
|
||||
|
|
||||
| **newState**: string
|
||||
| The new state. Must be the name of a state key as passed to :ref:`ref-registerfunction`.
|
||||
|
|
||||
|
||||
**Example**
|
||||
::
|
||||
|
||||
SetState(context, 'on')
|
||||
|
||||
|
||||
.. _ref-onsimconnect:
|
||||
|
||||
OnSimConnect
|
||||
~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
OnSimConnect(context, variables, variablesChangedCallback)
|
||||
|
||||
Registers a Lua function to be called when the specified SimConnect variable(s) change. For a list of variables please refer to `Simulation variables <https://msdn.microsoft.com/en-us/library/cc526981.aspx>`_.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| **context**
|
||||
| The context parameter as passed to setupCallback.
|
||||
|
|
||||
| **variables**: table
|
||||
| A table containing information about the simulation variables you want to monitor. Each key will be reflected in the 'data' table passed to the variablesChangedCallback. Each value is a Lua table describing the variable.
|
||||
|
|
||||
| **variable**: string
|
||||
| The name of the variable as described in `Simulation variables <https://msdn.microsoft.com/en-us/library/cc526981.aspx>`_.
|
||||
|
|
||||
| **data**: string
|
||||
| One of the :ref:`ref-simconnectdatatype` values.
|
||||
|
|
||||
| **variablesChangedCallback**: function
|
||||
| A Lua function which is called when the variable's value changes. It receives 2 parameters: 'context' and 'data'. The data parameter is a Lua table where each key corresponds to a variable defined in the 'variables' parameter and it's value is the current value of the simulation variable.
|
||||
|
|
||||
|
||||
**Example**
|
||||
|
||||
::
|
||||
|
||||
OnSimConnect(context,
|
||||
{
|
||||
autoPilotAvailable = { variable = 'AUTOPILOT AVAILABLE', type = SimConnectDataType.Bool },
|
||||
autoPilotAirspeed = { variable = 'AUTOPILOT AIRSPEED HOLD', type = SimConnectDataType.Bool }
|
||||
},
|
||||
function(context, data)
|
||||
if data.autoPilotAvailable then
|
||||
if data.autoPilotAirspeed then
|
||||
SetState(context, 'on')
|
||||
else
|
||||
SetState(context, 'off')
|
||||
end
|
||||
else
|
||||
SetState(context, 'notAvailable')
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
.. _ref-ontimer:
|
||||
|
||||
OnTimer
|
||||
~~~~~~~
|
||||
::
|
||||
|
||||
OnTimer(context, interval, timerCallback)
|
||||
|
||||
Registers a Lua function to be called when the specified interval elapses.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| **context**
|
||||
| The context parameter as passed to setupCallback.
|
||||
|
|
||||
| **interval**
|
||||
| The interval between calls to timerCallback in milliseconds. At the time of writing the minimum value is 100 milliseconds.
|
||||
|
|
||||
| **timerCallback**
|
||||
| A Lua function which is called when the interval elapses. It is passed a single parameter 'context'.
|
||||
|
|
||||
|
||||
**Example**
|
||||
|
||||
::
|
||||
|
||||
OnTimer(context, 1000,
|
||||
function(context)
|
||||
if FSXWindowVisible('ATC Menu') then
|
||||
SetState(context, 'visible')
|
||||
else
|
||||
SetState(context, 'hidden')
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
|
||||
.. _ref-windowvisible:
|
||||
|
||||
WindowVisible
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Checks if a window is currently visible. This is a thin wrapper around the FindWindow/FindWindowEx/IsWindowVisible Windows API. In the context of FSX panels you are probably looking for :ref:`ref-fsxwindowvisible`.
|
||||
|
||||
All parameters are optional, but at least one parameter is required. To skip a parameter simply pass nil instead.
|
||||
|
||||
To get a window's class name, use a tool like `Greatis Windowse <https://www.greatis.com/delphicb/windowse/>`_.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| **className**
|
||||
| The window class name of the window
|
||||
|
|
||||
| **title**
|
||||
| The title / caption / text of the window
|
||||
|
|
||||
| **parentClassName**
|
||||
| The parent window's class name. If specified, the first two parameters are considered to be a child window of this parent.
|
||||
|
|
||||
| **parentTitle**
|
||||
| The parent window's title / caption / text. If specified, the first two parameters are considered to be a child window of this parent.
|
||||
|
|
||||
|
||||
.. _ref-fsxwindowvisible:
|
||||
|
||||
FSXWindowVisible
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Checks if an FSX window is currently visible. Uses WindowVisible as a workaround because SimConnect does not expose this information directly.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| **title**
|
||||
| The title of the panel.
|
||||
|
|
||||
|
||||
Checks for both docked and undocked windows. Equal to:
|
||||
|
||||
::
|
||||
|
||||
WindowVisible('FS98CHILD', title, 'FS98MAIN') or WindowVisible('FS98FLOAT', title)
|
||||
|
||||
|
||||
|
||||
Global variables
|
||||
----------------
|
||||
|
||||
.. _ref-ledcolor:
|
||||
|
||||
LEDColor
|
||||
~~~~~~~~
|
||||
|
||||
**Keys**
|
||||
|
||||
- Off
|
||||
- Green
|
||||
- Amber
|
||||
- Red
|
||||
- FlashingGreenFast
|
||||
- FlashingGreenNormal
|
||||
- FlashingAmberFast
|
||||
- FlashingAmberNormal
|
||||
- FlashingRedFast
|
||||
- FlashingRedNormal
|
||||
|
||||
The 'Fast' flashing versions stay on and off for half a second, the 'Normal' version for 1 second.
|
||||
|
||||
**Example**
|
||||
|
||||
::
|
||||
|
||||
{ default = LEDColor.Green }
|
||||
|
||||
|
||||
.. _ref-simconnectdatatype:
|
||||
|
||||
SimConnectDataType
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**Keys**
|
||||
|
||||
- Float64
|
||||
- Float32
|
||||
- Int64
|
||||
- Int32
|
||||
- String
|
||||
- Bool
|
||||
- XYZ
|
||||
- LatLonAlt
|
||||
- Waypoint
|
||||
|
||||
The XYZ, LatLonAlt and Waypoint data types will return a table in the 'data' parameter for the OnSimConnect callback with the following keys:
|
||||
|
||||
**XYZ**
|
||||
|
||||
- X
|
||||
- Y
|
||||
- Z
|
||||
|
||||
**LatLonAlt**
|
||||
|
||||
- Latitude
|
||||
- Longitude
|
||||
- Altitude
|
||||
|
||||
**Waypoint**
|
||||
|
||||
- Latitude
|
||||
- Longitude
|
||||
- Altitude
|
||||
- KtsSpeed
|
||||
- PercentThrottle
|
||||
- Flags
|
||||
|
||||
The Flags value is a table containing the following keys, where each is a boolean:
|
||||
|
||||
- SpeedRequested
|
||||
- ThrottleRequested
|
||||
- ComputeVerticalSpeed
|
||||
- IsAGL
|
||||
- OnGround
|
||||
- Reverse
|
||||
- WrapToFirst
|
@ -340,7 +340,7 @@ type
|
||||
isvararg: Byte;
|
||||
istailcall: Byte;
|
||||
short_src: array[0..LUA_IDSIZE - 1] of AnsiChar;
|
||||
//struct CallInfo *i_ci; /* active function */
|
||||
i_ci: Integer;
|
||||
end;
|
||||
|
||||
{ Functions to be called by the debugger in specific events }
|
||||
|
@ -398,6 +398,13 @@ type
|
||||
|
||||
procedure OpenLibraries(ALibraries: TLuaLibraries); virtual;
|
||||
|
||||
{ Get or set the current path(s) used for require calls. Paths must be separated
|
||||
by semicolons and questions marks will be replaced with the requested file name,
|
||||
as per the Lua documentation at: https://www.lua.org/pil/8.1.html }
|
||||
function GetRequirePath: string;
|
||||
procedure SetRequirePath(const APath: string);
|
||||
procedure AddRequirePath(const APath: string);
|
||||
|
||||
{ These methods should only be called right after one of the
|
||||
LoadFrom methods, which must have AutoRun set to False. }
|
||||
procedure Run; virtual;
|
||||
@ -1400,6 +1407,51 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
function TLua.GetRequirePath: string;
|
||||
begin
|
||||
CheckState;
|
||||
|
||||
lua_getglobal(State, 'package');
|
||||
try
|
||||
lua_getfield(State, -1, 'path');
|
||||
try
|
||||
Result := TLuaHelpers.LuaToString(State, -1);
|
||||
finally
|
||||
lua_pop(State, 1);
|
||||
end;
|
||||
finally
|
||||
lua_pop(State, 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TLua.SetRequirePath(const APath: string);
|
||||
begin
|
||||
CheckState;
|
||||
|
||||
lua_getglobal(State, 'package');
|
||||
try
|
||||
TLuaHelpers.PushString(State, APath);
|
||||
lua_setfield(State, -2, 'path');
|
||||
finally
|
||||
lua_pop(State, 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TLua.AddRequirePath(const APath: string);
|
||||
var
|
||||
path: string;
|
||||
|
||||
begin
|
||||
path := GetRequirePath;
|
||||
if (Length(path) > 0) and (path[Length(path)] <> ';') then
|
||||
path := path + ';';
|
||||
|
||||
SetRequirePath(path + APath);
|
||||
end;
|
||||
|
||||
|
||||
procedure TLua.Run;
|
||||
begin
|
||||
CheckIsFunction;
|
||||
|
@ -1,7 +1,7 @@
|
||||
object MainForm: TMainForm
|
||||
Left = 0
|
||||
Top = 0
|
||||
ActiveControl = cmbProfiles
|
||||
ActiveControl = cbTrayIcon
|
||||
BorderIcons = [biSystemMenu, biMinimize]
|
||||
BorderStyle = bsSingle
|
||||
Caption = 'G940 LED Control'
|
||||
@ -30,7 +30,7 @@ object MainForm: TMainForm
|
||||
Margins.Top = 8
|
||||
Margins.Right = 8
|
||||
Margins.Bottom = 8
|
||||
ActivePage = tsButtons
|
||||
ActivePage = tsConfiguration
|
||||
Align = alClient
|
||||
TabOrder = 0
|
||||
object tsButtons: TTabSheet
|
||||
@ -375,10 +375,6 @@ object MainForm: TMainForm
|
||||
object tsConfiguration: TTabSheet
|
||||
Caption = ' Configuration '
|
||||
ImageIndex = 2
|
||||
ExplicitLeft = 0
|
||||
ExplicitTop = 0
|
||||
ExplicitWidth = 0
|
||||
ExplicitHeight = 0
|
||||
DesignSize = (
|
||||
442
|
||||
452)
|
||||
|
@ -228,6 +228,7 @@ type
|
||||
|
||||
implementation
|
||||
uses
|
||||
System.IOUtils,
|
||||
System.SysUtils,
|
||||
System.Types,
|
||||
System.Win.ComObj,
|
||||
@ -334,6 +335,12 @@ begin
|
||||
scriptPaths[0] := App.Path + FSXScriptsPath;
|
||||
scriptPaths[1] := App.UserPath + UserDataPath + FSXScriptsPath;
|
||||
|
||||
if DebugHook <> 0 then
|
||||
begin
|
||||
SetLength(scriptPaths, 3);
|
||||
scriptPaths[2] := TPath.GetFullPath(App.Path + '..\' + FSXScriptsPath);
|
||||
end;
|
||||
|
||||
FunctionRegistry.Register(TFSXLEDFunctionProvider.Create(scriptPaths));
|
||||
|
||||
|
||||
|
@ -51,7 +51,7 @@
|
||||
<PropertyGroup Condition="'$(Base)'!=''">
|
||||
<VerInfo_Release>6</VerInfo_Release>
|
||||
<VerInfo_MinorVer>1</VerInfo_MinorVer>
|
||||
<DCC_UsePackage>rtl;dbrtl;$(DCC_UsePackage)</DCC_UsePackage>
|
||||
<DCC_UsePackage>rtl;dbrtl;xmlrtl;$(DCC_UsePackage)</DCC_UsePackage>
|
||||
<DCC_DcuOutput>Lib</DCC_DcuOutput>
|
||||
<DCC_ExeOutput>Bin</DCC_ExeOutput>
|
||||
<DCC_UnitSearchPath>..\Shared;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
|
||||
@ -63,10 +63,12 @@
|
||||
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_Win64)'!=''">
|
||||
<DCC_UsePackage>vclx;vcl;vclactnband;$(DCC_UsePackage)</DCC_UsePackage>
|
||||
<Icon_MainIcon>G940LEDControl_Icon.ico</Icon_MainIcon>
|
||||
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_Win32)'!=''">
|
||||
<DCC_UsePackage>vclx;vcl;vclactnband;$(DCC_UsePackage)</DCC_UsePackage>
|
||||
<DCC_PACKAGE_NO_LINK>false</DCC_PACKAGE_NO_LINK>
|
||||
<DCC_UNIT_PLATFORM>false</DCC_UNIT_PLATFORM>
|
||||
<DCC_SYMBOL_PLATFORM>false</DCC_SYMBOL_PLATFORM>
|
||||
|
35
G940LEDControl/Scripts/FSX/autopilot.lua
Normal file
35
G940LEDControl/Scripts/FSX/autopilot.lua
Normal file
@ -0,0 +1,35 @@
|
||||
local strings = require './lib/strings'
|
||||
|
||||
|
||||
RegisterFunction(
|
||||
{
|
||||
uid = 'autoPilotAirspeed',
|
||||
category = strings.Category.FSX.AutoPilot,
|
||||
displayName = 'Autopilot airspeed',
|
||||
states = {
|
||||
on = { displayName = 'On', default = LEDColor.Green },
|
||||
off = { displayName = 'Off', default = LEDColor.Red },
|
||||
notAvailable = { displayName = 'Not available', default = LEDColor.Off }
|
||||
}
|
||||
},
|
||||
function(context)
|
||||
SetState(context, 'notAvailable')
|
||||
|
||||
OnSimConnect(context,
|
||||
{
|
||||
autoPilotAvailable = { variable = 'AUTOPILOT AVAILABLE', type = SimConnectDataType.Bool },
|
||||
autoPilotAirspeed = { variable = 'AUTOPILOT AIRSPEED HOLD', type = SimConnectDataType.Bool }
|
||||
},
|
||||
function(context, data)
|
||||
if data.autoPilotAvailable then
|
||||
if data.autoPilotAirspeed then
|
||||
SetState(context, 'on')
|
||||
else
|
||||
SetState(context, 'off')
|
||||
end
|
||||
else
|
||||
SetState(context, 'notAvailable')
|
||||
end
|
||||
end)
|
||||
end
|
||||
)
|
16
G940LEDControl/Scripts/FSX/lib/strings.lua
Normal file
16
G940LEDControl/Scripts/FSX/lib/strings.lua
Normal file
@ -0,0 +1,16 @@
|
||||
local strings = {}
|
||||
|
||||
strings.Category = {}
|
||||
|
||||
strings.Category.FSX = {}
|
||||
strings.Category.FSX.Default = 'Flight Simulator X'
|
||||
strings.Category.FSX.AutoPilot = strings.Category.FSX.Default..' - Autopilot'
|
||||
strings.Category.FSX.ControlSurfaces = strings.Category.FSX.Default..' - Control surfaces'
|
||||
strings.Category.FSX.Engines = strings.Category.FSX.Default..' - Engines'
|
||||
strings.Category.FSX.Instruments = strings.Category.FSX.Default..' - Instruments'
|
||||
strings.Category.FSX.Lights = strings.Category.FSX.Default..' - Lights'
|
||||
strings.Category.FSX.Panels = strings.Category.FSX.Default..' - Panels'
|
||||
strings.Category.FSX.Radios = strings.Category.FSX.Default..' - Radios'
|
||||
strings.Category.FSX.Systems = strings.Category.FSX.Default..' - Systems'
|
||||
|
||||
return strings
|
26
G940LEDControl/Scripts/FSX/panels.lua
Normal file
26
G940LEDControl/Scripts/FSX/panels.lua
Normal file
@ -0,0 +1,26 @@
|
||||
local strings = require './lib/strings'
|
||||
|
||||
|
||||
RegisterFunction(
|
||||
{
|
||||
uid = 'atcVisiblity',
|
||||
category = strings.Category.FSX.Panels,
|
||||
displayName = 'ATC Visibility',
|
||||
states = {
|
||||
hidden = { displayName = 'Hidden', default = LEDColor.Green },
|
||||
visible = { displayName = 'Visible', default = LEDColor.FlashingAmberNormal },
|
||||
}
|
||||
},
|
||||
function(context)
|
||||
SetState(context, 'hidden')
|
||||
|
||||
OnTimer(context, 1000,
|
||||
function(context)
|
||||
if FSXWindowVisible('ATC Menu') then
|
||||
SetState(context, 'visible')
|
||||
else
|
||||
SetState(context, 'hidden')
|
||||
end
|
||||
end)
|
||||
end
|
||||
)
|
@ -26,12 +26,14 @@ type
|
||||
FSimConnect: TInterfacedObject;
|
||||
FSimConnectLock: TCriticalSection;
|
||||
FProfileMenuSimConnect: IFSXSimConnectProfileMenu;
|
||||
FScriptSimConnect: TObject;
|
||||
protected
|
||||
function GetUID: string; override;
|
||||
function CreateLuaLEDFunction(AInfo: ILuaTable; AOnSetup: ILuaFunction): TCustomLuaLEDFunction; override;
|
||||
|
||||
procedure InitInterpreter; override;
|
||||
procedure ScriptOnSimConnect(Context: ILuaContext);
|
||||
procedure ScriptFSXWindowVisible(Context: ILuaContext);
|
||||
|
||||
procedure SetupWorker(AWorker: TFSXLEDFunctionWorker; AOnSetup: ILuaFunction);
|
||||
|
||||
{ IFSXSimConnectObserver }
|
||||
@ -132,18 +134,6 @@ type
|
||||
end;
|
||||
|
||||
|
||||
TLuaSimConnect = class(TPersistent)
|
||||
private
|
||||
FProvider: TFSXLEDFunctionProvider;
|
||||
protected
|
||||
property Provider: TFSXLEDFunctionProvider read FProvider;
|
||||
public
|
||||
constructor Create(AProvider: TFSXLEDFunctionProvider);
|
||||
published
|
||||
procedure Monitor(Context: ILuaContext);
|
||||
end;
|
||||
|
||||
|
||||
const
|
||||
LuaSimConnectDataTypes: array[TLuaSimConnectDataType] of string =
|
||||
(
|
||||
@ -173,7 +163,6 @@ end;
|
||||
constructor TFSXLEDFunctionProvider.Create(const AScriptFolders: TStringDynArray);
|
||||
begin
|
||||
FSimConnectLock := TCriticalSection.Create;
|
||||
FScriptSimConnect := TLuaSimConnect.Create(Self);
|
||||
|
||||
inherited Create(AScriptFolders);
|
||||
end;
|
||||
@ -183,7 +172,6 @@ destructor TFSXLEDFunctionProvider.Destroy;
|
||||
begin
|
||||
inherited Destroy;
|
||||
|
||||
FreeAndNil(FScriptSimConnect);
|
||||
FreeAndNil(FSimConnectLock);
|
||||
end;
|
||||
|
||||
@ -271,7 +259,8 @@ var
|
||||
begin
|
||||
inherited InitInterpreter;
|
||||
|
||||
Interpreter.RegisterFunctions(FScriptSimConnect, 'SimConnect');
|
||||
Interpreter.RegisterFunction('OnSimConnect', ScriptOnSimConnect);
|
||||
Interpreter.RegisterFunction('FSXWindowVisible', ScriptFSXWindowVisible);
|
||||
|
||||
simConnectDataType := TLuaTable.Create;
|
||||
for dataType := Low(TLuaSimConnectDataType) to High(TLuaSimConnectDataType) do
|
||||
@ -281,6 +270,116 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TFSXLEDFunctionProvider.ScriptOnSimConnect(Context: ILuaContext);
|
||||
var
|
||||
workerID: string;
|
||||
variables: ILuaTable;
|
||||
onData: ILuaFunction;
|
||||
worker: TCustomLuaLEDFunctionWorker;
|
||||
definition: IFSXSimConnectDefinition;
|
||||
variable: TLuaKeyValuePair;
|
||||
info: ILuaTable;
|
||||
dataType: TLuaSimConnectDataType;
|
||||
simConnectDataType: SIMCONNECT_DATAType;
|
||||
units: string;
|
||||
luaVariables: TList<TLuaSimConnectVariable>;
|
||||
luaVariable: TLuaSimConnectVariable;
|
||||
|
||||
begin
|
||||
CheckParameters('OnSimConnect', Context.Parameters, [VariableString, VariableTable, VariableFunction]);
|
||||
|
||||
workerID := Context.Parameters[0].AsString;
|
||||
variables := Context.Parameters[1].AsTable;
|
||||
onData := Context.Parameters[2].AsFunction;
|
||||
|
||||
worker := FindWorker(workerID);
|
||||
if not Assigned(worker) then
|
||||
raise ELuaScriptError.Create('OnSimConnect: invalid context');
|
||||
|
||||
definition := GetSimConnect.CreateDefinition;
|
||||
|
||||
luaVariables := TList<TLuaSimConnectVariable>.Create;
|
||||
try
|
||||
for variable in variables do
|
||||
begin
|
||||
if variable.Value.VariableType = VariableTable then
|
||||
begin
|
||||
info := variable.Value.AsTable;
|
||||
if info.HasValue('variable') then
|
||||
begin
|
||||
luaVariable.Name := variable.Key.AsString;
|
||||
units := '';
|
||||
simConnectDataType := SIMCONNECT_DATAType_FLOAT64;
|
||||
|
||||
if info.HasValue('type') and GetDataType(info.GetValue('type').AsString, dataType) then
|
||||
begin
|
||||
luaVariable.DataType := dataType;
|
||||
|
||||
case dataType of
|
||||
Float32: simConnectDataType := SIMCONNECT_DATAType_FLOAT32;
|
||||
Int64: simConnectDataType := SIMCONNECT_DATAType_INT64;
|
||||
Int32,
|
||||
Bool:
|
||||
begin
|
||||
simConnectDataType := SIMCONNECT_DATAType_INT32;
|
||||
units := 'bool';
|
||||
end;
|
||||
|
||||
// TODO change to STRINGV
|
||||
StringValue: simConnectDataType := SIMCONNECT_DATAType_STRING256;
|
||||
XYZ: simConnectDataType := SIMCONNECT_DATAType_XYZ;
|
||||
LatLonAlt: simConnectDataType := SIMCONNECT_DATAType_LATLONALT;
|
||||
Waypoint: simConnectDataType := SIMCONNECT_DATAType_WAYPOINT;
|
||||
end;
|
||||
|
||||
if info.HasValue('units') then
|
||||
units := info.GetValue('units').AsString
|
||||
else if not (dataType in [Bool, StringValue, XYZ, LatLonAlt, Waypoint]) then
|
||||
raise ELuaScriptError.CreateFmt('OnSimConnect: missing units for variable %s', [variable.Key.AsString]);
|
||||
end else
|
||||
begin
|
||||
if not info.HasValue('units') then
|
||||
raise ELuaScriptError.CreateFmt('OnSimConnect: missing units or type for variable %s', [variable.Key.AsString]);
|
||||
|
||||
units := info.GetValue('units').AsString;
|
||||
end;
|
||||
|
||||
luaVariables.Add(luaVariable);
|
||||
definition.AddVariable(info.GetValue('variable').AsString, units, simConnectDataType);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
(worker as TFSXLEDFunctionWorker).AddDefinition(definition, TFSXFunctionWorkerDataHandler.Create(luaVariables, worker.UID, onData));
|
||||
finally
|
||||
FreeAndNil(luaVariables);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TFSXLEDFunctionProvider.ScriptFSXWindowVisible(Context: ILuaContext);
|
||||
const
|
||||
ClassNameMainWindow = 'FS98MAIN';
|
||||
ClassNameChildWindow = 'FS98CHILD';
|
||||
ClassNameFloatWindow = 'FS98FLOAT';
|
||||
|
||||
var
|
||||
windowTitle: string;
|
||||
|
||||
begin
|
||||
CheckParameters('FSXWindowVisible', Context.Parameters, [VariableString]);
|
||||
windowTitle := Context.Parameters[0].AsString;
|
||||
|
||||
Context.Result.Push(
|
||||
{ Docked }
|
||||
WindowVisible(ClassNameChildWindow, windowTitle, ClassNameMainWindow, '') or
|
||||
|
||||
{ Undocked }
|
||||
WindowVisible(ClassNameFloatWindow, windowTitle, '', ''));
|
||||
end;
|
||||
|
||||
|
||||
// #ToDo1 -oMvR: 4-6-2017: move up to LuaLEDFunctionProvider
|
||||
procedure TFSXLEDFunctionProvider.SetupWorker(AWorker: TFSXLEDFunctionWorker; AOnSetup: ILuaFunction);
|
||||
begin
|
||||
try
|
||||
@ -562,110 +661,4 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{ TLuaSimConnect }
|
||||
constructor TLuaSimConnect.Create(AProvider: TFSXLEDFunctionProvider);
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
FProvider := AProvider;
|
||||
end;
|
||||
|
||||
|
||||
procedure TLuaSimConnect.Monitor(Context: ILuaContext);
|
||||
var
|
||||
workerID: string;
|
||||
variables: ILuaTable;
|
||||
onData: ILuaFunction;
|
||||
worker: TCustomLuaLEDFunctionWorker;
|
||||
definition: IFSXSimConnectDefinition;
|
||||
variable: TLuaKeyValuePair;
|
||||
info: ILuaTable;
|
||||
dataType: TLuaSimConnectDataType;
|
||||
simConnectDataType: SIMCONNECT_DATAType;
|
||||
units: string;
|
||||
luaVariables: TList<TLuaSimConnectVariable>;
|
||||
luaVariable: TLuaSimConnectVariable;
|
||||
|
||||
begin
|
||||
if Context.Parameters.Count < 3 then
|
||||
raise ELuaScriptError.Create('Not enough parameters for SimConnect.Monitor');
|
||||
|
||||
if Context.Parameters[0].VariableType <> VariableString then
|
||||
raise ELuaScriptError.Create('Context expected for SimConnect.Monitor parameter 1');
|
||||
|
||||
if Context.Parameters[1].VariableType <> VariableTable then
|
||||
raise ELuaScriptError.Create('Table expected for SimConnect.Monitor parameter 2');
|
||||
|
||||
if Context.Parameters[2].VariableType <> VariableFunction then
|
||||
raise ELuaScriptError.Create('Function expected for SimConnect.Monitor parameter 3');
|
||||
|
||||
workerID := Context.Parameters[0].AsString;
|
||||
variables := Context.Parameters[1].AsTable;
|
||||
onData := Context.Parameters[2].AsFunction;
|
||||
|
||||
worker := Provider.FindWorker(workerID);
|
||||
if not Assigned(worker) then
|
||||
raise ELuaScriptError.Create('Context expected for SimConnect.Monitor parameter 1');
|
||||
|
||||
definition := Provider.GetSimConnect.CreateDefinition;
|
||||
|
||||
luaVariables := TList<TLuaSimConnectVariable>.Create;
|
||||
try
|
||||
for variable in variables do
|
||||
begin
|
||||
if variable.Value.VariableType = VariableTable then
|
||||
begin
|
||||
info := variable.Value.AsTable;
|
||||
if info.HasValue('variable') then
|
||||
begin
|
||||
luaVariable.Name := variable.Key.AsString;
|
||||
units := '';
|
||||
simConnectDataType := SIMCONNECT_DATAType_FLOAT64;
|
||||
|
||||
if info.HasValue('type') and GetDataType(info.GetValue('type').AsString, dataType) then
|
||||
begin
|
||||
luaVariable.DataType := dataType;
|
||||
|
||||
case dataType of
|
||||
Float32: simConnectDataType := SIMCONNECT_DATAType_FLOAT32;
|
||||
Int64: simConnectDataType := SIMCONNECT_DATAType_INT64;
|
||||
Int32,
|
||||
Bool:
|
||||
begin
|
||||
simConnectDataType := SIMCONNECT_DATAType_INT32;
|
||||
units := 'bool';
|
||||
end;
|
||||
|
||||
// TODO change to STRINGV
|
||||
StringValue: simConnectDataType := SIMCONNECT_DATAType_STRING256;
|
||||
XYZ: simConnectDataType := SIMCONNECT_DATAType_XYZ;
|
||||
LatLonAlt: simConnectDataType := SIMCONNECT_DATAType_LATLONALT;
|
||||
Waypoint: simConnectDataType := SIMCONNECT_DATAType_WAYPOINT;
|
||||
end;
|
||||
|
||||
if info.HasValue('units') then
|
||||
units := info.GetValue('units').AsString
|
||||
else if not (dataType in [Bool, StringValue, XYZ, LatLonAlt, Waypoint]) then
|
||||
raise ELuaScriptError.CreateFmt('Missing units for variable %s', [variable.Key.AsString]);
|
||||
end else
|
||||
begin
|
||||
if not info.HasValue('units') then
|
||||
raise ELuaScriptError.CreateFmt('Missing units or type for variable %s', [variable.Key.AsString]);
|
||||
|
||||
units := info.GetValue('units').AsString;
|
||||
end;
|
||||
|
||||
luaVariables.Add(luaVariable);
|
||||
definition.AddVariable(info.GetValue('variable').AsString, units, simConnectDataType);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
(worker as TFSXLEDFunctionWorker).AddDefinition(definition, TFSXFunctionWorkerDataHandler.Create(luaVariables, worker.UID, onData));
|
||||
finally
|
||||
FreeAndNil(luaVariables);
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
@ -6,6 +6,8 @@ uses
|
||||
System.SysUtils,
|
||||
System.Types,
|
||||
|
||||
OtlTask,
|
||||
OtlTaskControl,
|
||||
X2Log.Intf,
|
||||
|
||||
LEDFunction,
|
||||
@ -52,6 +54,7 @@ type
|
||||
protected
|
||||
function CreateLuaLEDFunction(AInfo: ILuaTable; ASetup: ILuaFunction): TCustomLuaLEDFunction; virtual; abstract;
|
||||
|
||||
procedure CheckParameters(const AFunctionName: string; AParameters: ILuaReadParameters; AExpectedTypes: array of TLuaVariableType);
|
||||
procedure AppendVariable(ABuilder: TStringBuilder; AVariable: ILuaVariable);
|
||||
procedure AppendTable(ABuilder: TStringBuilder; ATable: ILuaTable);
|
||||
|
||||
@ -60,6 +63,10 @@ type
|
||||
|
||||
procedure ScriptRegisterFunction(Context: ILuaContext);
|
||||
procedure ScriptSetState(Context: ILuaContext);
|
||||
procedure ScriptOnTimer(Context: ILuaContext);
|
||||
procedure ScriptWindowVisible(Context: ILuaContext);
|
||||
|
||||
function WindowVisible(const AClassName, AWindowTitle, AParentClassName, AParentWindowTitle: string): Boolean;
|
||||
|
||||
procedure InitInterpreter; virtual;
|
||||
procedure RegisterFunctions; override;
|
||||
@ -81,10 +88,14 @@ type
|
||||
private
|
||||
FProvider: TCustomLuaLEDFunctionProvider;
|
||||
FUID: string;
|
||||
FTasks: TList<IOmniTaskControl>;
|
||||
|
||||
procedure SetProvider(const Value: TCustomLuaLEDFunctionProvider);
|
||||
protected
|
||||
procedure AddTask(ATask: IOmniTaskControl);
|
||||
|
||||
property Provider: TCustomLuaLEDFunctionProvider read FProvider write SetProvider;
|
||||
property Tasks: TList<IOmniTaskControl> read FTasks;
|
||||
public
|
||||
constructor Create(const AProviderUID, AFunctionUID: string; AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; const APreviousState: string = ''); override;
|
||||
destructor Destroy; override;
|
||||
@ -97,6 +108,8 @@ implementation
|
||||
uses
|
||||
System.Classes,
|
||||
System.IOUtils,
|
||||
System.StrUtils,
|
||||
Winapi.Windows,
|
||||
|
||||
Lua.API,
|
||||
X2Log.Global,
|
||||
@ -123,6 +136,18 @@ type
|
||||
end;
|
||||
|
||||
|
||||
TLuaTimerTask = class(TOmniWorker)
|
||||
private
|
||||
FOnTimer: TProc;
|
||||
protected
|
||||
property OnTimer: TProc read FOnTimer;
|
||||
public
|
||||
constructor Create(AOnTimer: TProc);
|
||||
|
||||
procedure Run;
|
||||
end;
|
||||
|
||||
|
||||
const
|
||||
LuaLEDColors: array[TLEDColor] of string =
|
||||
(
|
||||
@ -173,12 +198,33 @@ end;
|
||||
|
||||
procedure TCustomLuaLEDFunctionProvider.InitInterpreter;
|
||||
var
|
||||
requirePath: TStringBuilder;
|
||||
scriptFolder: string;
|
||||
table: ILuaTable;
|
||||
color: TLEDColor;
|
||||
|
||||
begin
|
||||
requirePath := TStringBuilder.Create;
|
||||
try
|
||||
for scriptFolder in ScriptFolders do
|
||||
begin
|
||||
if requirePath.Length > 0 then
|
||||
requirePath.Append(';');
|
||||
|
||||
requirePath.Append(IncludeTrailingPathDelimiter(scriptFolder)).Append('?;')
|
||||
.Append(IncludeTrailingPathDelimiter(scriptFolder)).Append('?.lua');
|
||||
end;
|
||||
|
||||
Interpreter.SetRequirePath(requirePath.ToString);
|
||||
Interpreter.GetRequirePath;
|
||||
finally
|
||||
FreeAndNil(requirePath);
|
||||
end;
|
||||
|
||||
Interpreter.RegisterFunction('RegisterFunction', ScriptRegisterFunction);
|
||||
Interpreter.RegisterFunction('SetState', ScriptSetState);
|
||||
Interpreter.RegisterFunction('OnTimer', ScriptOnTimer);
|
||||
Interpreter.RegisterFunction('WindowVisible', ScriptWindowVisible);
|
||||
|
||||
Interpreter.RegisterFunctions(FScriptLog, 'Log');
|
||||
|
||||
@ -187,9 +233,30 @@ begin
|
||||
table.SetValue(LuaLEDColors[color], LuaLEDColors[color]);
|
||||
|
||||
Interpreter.SetGlobalVariable('LEDColor', table);
|
||||
end;
|
||||
|
||||
// #ToDo1 -oMvR: 29-5-2017: Timer
|
||||
// #ToDo1 -oMvR: 29-5-2017: FindWindow / FindFSXWindow
|
||||
|
||||
procedure TCustomLuaLEDFunctionProvider.CheckParameters(const AFunctionName: string; AParameters: ILuaReadParameters; AExpectedTypes: array of TLuaVariableType);
|
||||
const
|
||||
VariableTypeName: array[TLuaVariableType] of string =
|
||||
(
|
||||
'None', 'Boolean', 'Integer',
|
||||
'Number', 'UserData', 'String',
|
||||
'Table', 'Function'
|
||||
);
|
||||
|
||||
var
|
||||
parameterIndex: Integer;
|
||||
|
||||
begin
|
||||
if AParameters.Count < Length(AExpectedTypes) then
|
||||
raise ELuaScriptError.CreateFmt('%s: expected at least %d parameter%s', [AFunctionName, Length(AExpectedTypes), IfThen(Length(AExpectedTypes) <> 1, 's', '')]);
|
||||
|
||||
for parameterIndex := 0 to High(AExpectedTypes) do
|
||||
if AParameters[parameterIndex].VariableType <> AExpectedTypes[parameterIndex] then
|
||||
raise ELuaScriptError.CreateFmt('%s: expected %s for parameter %d, got %s',
|
||||
[AFunctionName, VariableTypeName[AExpectedTypes[parameterIndex]],
|
||||
Succ(parameterIndex), VariableTypeName[AParameters[parameterIndex].VariableType]]);
|
||||
end;
|
||||
|
||||
|
||||
@ -280,20 +347,13 @@ var
|
||||
setup: ILuaFunction;
|
||||
|
||||
begin
|
||||
if Context.Parameters.Count < 2 then
|
||||
raise ELuaScriptError.Create('Not enough parameters for RegisterFunction');
|
||||
|
||||
if Context.Parameters[0].VariableType <> VariableTable then
|
||||
raise ELuaScriptError.Create('Table expected for RegisterFunction parameter 1');
|
||||
|
||||
if Context.Parameters[1].VariableType <> VariableFunction then
|
||||
raise ELuaScriptError.Create('Function expected for RegisterFunction parameter 2');
|
||||
CheckParameters('RegisterFunction', Context.Parameters, [VariableTable, VariableFunction]);
|
||||
|
||||
info := Context.Parameters[0].AsTable;
|
||||
setup := Context.Parameters[1].AsFunction;
|
||||
|
||||
if not info.HasValue('uid') then
|
||||
raise ELuaScriptError.Create('"uid" value is required for RegisterFunction parameter 1');
|
||||
raise ELuaScriptError.Create('RegisterFunction: "uid" value is required');
|
||||
|
||||
DoLogMessage(Context, TX2LogLevel.Info, Format('Registering function: %s', [info.GetValue('uid').AsString]));
|
||||
RegisterFunction(CreateLuaLEDFunction(info, setup));
|
||||
@ -307,27 +367,114 @@ var
|
||||
stateUID: string;
|
||||
|
||||
begin
|
||||
if Context.Parameters.Count < 2 then
|
||||
raise ELuaScriptError.Create('Not enough parameters for SetState');
|
||||
|
||||
if Context.Parameters[0].VariableType <> VariableString then
|
||||
raise ELuaScriptError.Create('Context expected for SetState parameter 1');
|
||||
|
||||
if Context.Parameters[1].VariableType <> VariableString then
|
||||
raise ELuaScriptError.Create('State expected for SetState parameter 2');
|
||||
CheckParameters('SetState', Context.Parameters, [VariableString, VariableString]);
|
||||
|
||||
workerID := Context.Parameters[0].AsString;
|
||||
stateUID := Context.Parameters[1].AsString;
|
||||
|
||||
worker := FindWorker(workerID);
|
||||
if not Assigned(worker) then
|
||||
raise ELuaScriptError.Create('Context expected for SetState parameter 1');
|
||||
raise ELuaScriptError.Create('SetState: invalid context');
|
||||
|
||||
DoLogMessage(Context, TX2LogLevel.Info, Format('Setting state for %s to: %s', [worker.GetFunctionUID, stateUID]));
|
||||
worker.SetCurrentState(stateUID);
|
||||
end;
|
||||
|
||||
|
||||
procedure TCustomLuaLEDFunctionProvider.ScriptOnTimer(Context: ILuaContext);
|
||||
var
|
||||
workerID: string;
|
||||
interval: Integer;
|
||||
timerCallback: ILuaFunction;
|
||||
worker: TCustomLuaLEDFunctionWorker;
|
||||
|
||||
begin
|
||||
CheckParameters('OnTimer', Context.Parameters, [VariableString, VariableNumber, VariableFunction]);
|
||||
|
||||
workerID := Context.Parameters[0].AsString;
|
||||
interval := Context.Parameters[1].AsInteger;
|
||||
timerCallback := Context.Parameters[2].AsFunction;
|
||||
|
||||
worker := FindWorker(workerID);
|
||||
if not Assigned(worker) then
|
||||
raise ELuaScriptError.Create('OnTimer: invalid context');
|
||||
|
||||
DoLogMessage(Context, TX2LogLevel.Info, Format('Adding timer for %s, interval: %d', [worker.GetFunctionUID, interval]));
|
||||
worker.AddTask(CreateTask(TLuaTimerTask.Create(
|
||||
procedure
|
||||
begin
|
||||
try
|
||||
timerCallback.Call([workerID]);
|
||||
except
|
||||
on E:Exception do
|
||||
TX2GlobalLog.Category('Lua').Exception(E);
|
||||
end;
|
||||
end))
|
||||
.SetTimer(1, MSecsPerSec, @TLuaTimerTask.Run)
|
||||
.Run);
|
||||
end;
|
||||
|
||||
|
||||
procedure TCustomLuaLEDFunctionProvider.ScriptWindowVisible(Context: ILuaContext);
|
||||
var
|
||||
className: string;
|
||||
windowTitle: string;
|
||||
parentClassName: string;
|
||||
parentWindowTitle: string;
|
||||
|
||||
begin
|
||||
if Context.Parameters.Count = 0 then
|
||||
raise ELuaScriptError.Create('WindowVisible: expected at least 1 parameter');
|
||||
|
||||
className := '';
|
||||
windowTitle := '';
|
||||
parentClassName := '';
|
||||
parentWindowTitle := '';
|
||||
|
||||
if Context.Parameters.Count >= 1 then className := Context.Parameters[0].AsString;
|
||||
if Context.Parameters.Count >= 2 then windowTitle := Context.Parameters[1].AsString;
|
||||
if Context.Parameters.Count >= 3 then parentClassName := Context.Parameters[2].AsString;
|
||||
if Context.Parameters.Count >= 4 then parentWindowTitle := Context.Parameters[3].AsString;
|
||||
|
||||
Context.Result.Push(WindowVisible(className, windowTitle, parentClassName, parentWindowTitle));
|
||||
end;
|
||||
|
||||
|
||||
function TCustomLuaLEDFunctionProvider.WindowVisible(const AClassName, AWindowTitle, AParentClassName, AParentWindowTitle: string): Boolean;
|
||||
|
||||
function GetNilPChar(const AValue: string): PChar;
|
||||
begin
|
||||
if Length(AValue) > 0 then
|
||||
Result := PChar(AValue)
|
||||
else
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
var
|
||||
parentWindow: THandle;
|
||||
childWindow: THandle;
|
||||
window: THandle;
|
||||
|
||||
begin
|
||||
Result := False;
|
||||
|
||||
if (Length(AParentClassName) > 0) or (Length(AParentWindowTitle) > 0) then
|
||||
begin
|
||||
parentWindow := FindWindow(GetNilPChar(AParentClassName), GetNilPChar(AParentWindowTitle));
|
||||
if parentWindow <> 0 then
|
||||
begin
|
||||
childWindow := FindWindowEx(parentWindow, 0, GetNilPChar(AClassName), GetNilPChar(AWindowTitle));
|
||||
Result := (childWindow <> 0) and IsWindowVisible(childWindow);
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
window := FindWindow(GetNilPChar(AClassName), GetNilPChar(AWindowTitle));
|
||||
Result := (window <> 0) and IsWindowVisible(window);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
procedure TCustomLuaLEDFunctionProvider.RegisterFunctions;
|
||||
var
|
||||
scriptFolder: string;
|
||||
@ -461,13 +608,36 @@ begin
|
||||
end;
|
||||
|
||||
destructor TCustomLuaLEDFunctionWorker.Destroy;
|
||||
var
|
||||
task: IOmniTaskControl;
|
||||
|
||||
begin
|
||||
if Assigned(Tasks) then
|
||||
begin
|
||||
for task in Tasks do
|
||||
begin
|
||||
task.Stop;
|
||||
task.WaitFor(INFINITE);
|
||||
end;
|
||||
|
||||
FreeAndNil(FTasks);
|
||||
end;
|
||||
|
||||
SetProvider(nil);
|
||||
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
|
||||
procedure TCustomLuaLEDFunctionWorker.AddTask(ATask: IOmniTaskControl);
|
||||
begin
|
||||
if not Assigned(Tasks) then
|
||||
FTasks := TList<IOmniTaskControl>.Create;
|
||||
|
||||
Tasks.Add(ATask);
|
||||
end;
|
||||
|
||||
|
||||
procedure TCustomLuaLEDFunctionWorker.SetProvider(const Value: TCustomLuaLEDFunctionProvider);
|
||||
begin
|
||||
if Value <> FProvider then
|
||||
@ -515,4 +685,19 @@ begin
|
||||
OnLog(Context, TX2LogLevel.Error);
|
||||
end;
|
||||
|
||||
|
||||
{ TLuaTimerTask }
|
||||
constructor TLuaTimerTask.Create(AOnTimer: TProc);
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
FOnTimer := AOnTimer;
|
||||
end;
|
||||
|
||||
|
||||
procedure TLuaTimerTask.Run;
|
||||
begin
|
||||
FOnTimer();
|
||||
end;
|
||||
|
||||
end.
|
||||
|
Loading…
Reference in New Issue
Block a user