Lua (/ˈluːə/ LOO-ə; from Portuguese: lua [ˈlu.(w)ɐ] meaning moon)[a] is a lightweight, multi-paradigm programming language designed primarily for embedded use in applications.[2] Lua is cross-platform, since the interpreter is written in ANSI C,[3] and has a relatively simple C API.[4] Lua was originally designed in 1993 as a language for extending software applications to meet the increasing demand for customization at the time. It provided the basic facilities of most procedural programming languages, but more complicated or domain-specific features were not included; rather, it included mechanisms for extending the language, allowing programmers to implement such features. As Lua was intended to be a general embeddable extension language, the designers of Lua focused on improving its speed, portability, extensibility, and ease-of-use in development. Lua was created in 1993 by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes, members of the Computer Graphics Technology Group (Tecgraf) at the Pontifical Catholic University of Rio de Janeiro, in Brazil. |
Lua is a powerful, efficient, lightweight, embeddable scripting language. It supports procedural programming, object-oriented programming, functional programming, data-driven programming, and data description. Lua combines simple procedural syntax with powerful data description constructs based on associative arrays and extensible semantics. Lua is dynamically typed, runs by interpreting bytecode with a register-based virtual machine, and has automatic memory management with incremental garbage collection, making it ideal for configuration, scripting, and rapid prototyping. |
Lua is an embedded language. This means that Lua is not a stand-alone application, but a library that we can link with other applications to incorporate Lua facilities into them. |
To access the Program Control window, press the [Prog Ctrl] button on the main window to access PC1. You can also right click on the button to access the popup menu. On the popup menu, you can select any of the 10 available PC windows. | |
The top of the window allows you to select the script file to
use. Press the [Browse] button to select a file. You can also use the
dropdown button to reselect a previous file. The program expects files
to have the ".lua" extension. The next set of buttons are used to select the function. Press [Run Script] to compile and run a script. Press [Compile Script] to just compile a script. Press [Interpreter] to open an Lua command line interpreter. Next are the function buttons and the [STOP] button. You have 30 function buttons which you can program to perform operations. Not shown in this older screen capture are three tabs each of which selects 10 function buttons. The [STOP] button stops the script. On the right are counters. There are 7 counters. The top one always shows the run time in seconds. The next three are used by the RFID readers if they are enabled on the Setup window. There are three more not shown just below these. These 3 and the 3 RFID counters can by used by your script to display information. The [Reset] button, clears the 6 counters to zero. A little further down on this page is a screen capture which shows the 7 counters. The Messages box displays information from the script using the print() command. [Copy All] copies all of the messages to the Windows clipboard. [Save] writes the messages to a file. [Clear] clears the message box. |
|
The
Detected Tags window can be displayed if the RFID system is enabled. It
shows all previously detected and newly detected tags. To view the
Detected Tags window, press the [Show Tags] button on the Program Control window. The tab "Engine Data Tags" shows tags that have been programmed with engine/car information. The tab "UID Only Tags" shows tags that have not been programmed and only contain the tag UID. |
|
If you right click on the Script File input box, you will get
this popup menu. Select [Edit this Script] to pass the script file to
an editor. The program tries to find these four editors in this order -
Notepad++, metapad, notepad, and wordpad. I recommend Notepad++ for editing Lua files as it understands the syntax of Lua and does appropriate syntax highlighting. Notepad and wordpad come with all versions of Windows. You can remove a script from the dropdown list or completely clear the list. |
|
You can't disconnect from the TIU when a script is running. This message reminds you to press the [STOP] button before you try to disconnect. | |
This is what the window looks like if you select the "Counter
Label Test.lua" script and press the [Compile Script]
button. That test
script defines 10 helper function buttons and all 6 available
counters. I'll describe how to use these below. The newest version of
RTC supports 30 buttons spread over 3 tabs. The message in the Messages window lets you know that the script compiled successfully. Notice here that the "RFID Enabled" LED is visible and green to indicate that the RFID train detection system is enabled. [Emergency Stop] sends instructions to all 5 possible TIU's to cut off all power to the track. [Prev] and [Next] show the previous and next Program Control windows. There can be up to 10 of them. [Hide] minimizes the window but does not otherwise affect any running script. [Close] terminates the window and releases all used memory. |
|
This shows the window after you press the [Run Script]
button. Messages appear from the script and the counters begin to
increment under control of the script. When you press [Run Script], the script is compiled first so you don't have press [Compile Script]. The Run Time counter turns green to indicate that the script is running. The [STOP] button is enabled. |
|
The user has pressed the [STOP] button and the script has stopped. Shown on the left for the first time are 2 of the 4 available LED's. These can be programmed by the script to display in red, green or yellow along with a label. I'll describe how to use these below. | |
This shows the window after the [Interpreter] button is
pushed. A command input line opens at the bottom of the Messages
window. You can type lua expressions and statements and they will be
executed. Look at the Interpreter section below for details. You can use the command input line dropdown list to select previous commands. You can also use the up arrow and down arrow keys. |
|
If you use the InputDropDown, InputBox or InputQuery functions, a box like this will open where the user can type a response. The window title, prompt, and default value can be programmed. | |
Here is what the Program Control window will look like if you are not using the RFID train detection system. |
✅ setup(Engine, TIU) -- is called once at the start, it is passed the engine number (1-99) and the TIU number (1-5) from the main RTC window. This function MUST be present in your script. Return true on success, false on failure or to end the script. If this function returns false, loop() and tag() will not be run. If this function returns false, cleanup() will be run. Note that the TIU number passed may not the TIU associated with the given engine number. Use GetTIUNo(Engine) to get that TIU number. You should save the Engine and TIU numbers in local variables as you will probably need them in other script functions. |
✅ loop(Engine, TIU) -- This function is NOT called during setup() or cleanup(). Once setup() completes, this function is called over and over as long it returns true. Return false to break out of the loop (you must still press [STOP] to run cleanup() and end the script). If the function does not appear in your script, RTC does not attempt to call it. loop() is called in both non-RFID and RFID enabled scripts. loop() will use up 100% of the CPU as it is called over and over. That will make the User Interface (UI) unresponsive. If this function returns true, you should always call Sleep() or Yield() at least once to give other parts of RTC time to run. |
✅ tag(Detector, Reader, EngineNo, TagLocation, CarModel, CarNumber, TagPacket) -- is called each time a tag is detected by the RFID tag readers. Requires the RFID Train Detection system to be enabled on the setup window. On the Setup window, the check box for [X ] Enable RFID must be checked. Passes in the detector number, the reader number, three decoded fields from the tag packet received and the complete tag packet itself from which you can decode the other fields.
The helper script "functions.lua" contains functions to help you decode the values passed into the tag() function. |
✅ tick(Engine, TIU) -- is called approximately once per second. This function is NOT called during setup() or cleanup(). Return true on success, false on failure at which time the tick() function will no longer be called but the rest of the script will continue to run. RTC does not guarantee this routine is called exactly once per second. If RTC script processing is extensive, a tick may be skipped. Always use RunTime() to get the exact time. If the function does not appear in your script, RTC does not attempt to call it. Do not call the Sleep() function from this function. |
✅ cleanup(Engine, TIU) -- is called once when the user presses the [STOP] button on the Program Control window. Return true on success, false on failure. If the function does not appear in your script, RTC does not attempt to call it. |
✅ event(FromPCNo, P1, P2, P3) -- is called by any PC window using the SetEvent() function. It is used for commnications between the PC windows. Return true on success, false on failure. "FromPCNo" is the Program Control window # of the script which executed the SetEvent() function. P1, P2 & P3 are integers which are being sent along with the event. Look at the SetEvent() description below for details of how this function is used. If the function does not appear in your script, RTC does not attempt to call it. |
Here is a example listing
of a simple script DoNothing Loop Test.lua
(this may not be as up to date as the zip file linked to below): -- Semicolons are not required in Lua code |
Here is a example listing
of a simple script DoNothing Tag Test.lua
(this may not be as up to date as the zip file linked to below): -- Semicolons are not required in Lua code |
Here is a example listing
of a tick function
--[[---------------------------------------------------------------------------------------]] |
Functions that interact with the Program Control window-- Program Control "Messages" Window. Displays in Black. -- To display a formated string, use print(string.format(fmt, vars, ..)) -- Multiple arguments, which can be strings, booleans, integers or numbers, are -- concatenated for display: print("TIU ", TIUNo, " Engine ", EngineNo); print("TIU " .. TIUNo .. " Engine " .. EngineNo); -- these two print functions calls produce the same result printc(color, ...); -- same as print(...) but displays in color -- Colors are defined in "defines.lua" and are: -- clRed -- clGreen -- clYellow -- clBlue -- clFuchsia -- clBlack (default if the color is not defined) BumpCounter(ID); -- Increments one of the counters on the Program Control window by 1 DecCounter(ID); -- Decrements one of the counters by 1 GetCounter(ID); -- Returns the value in one of the counters ClearCounter(ID); -- Sets one of the counters to zero. SetCounter(ID, value); -- sets one of the counters to the value given -- Returns true on success -- On Error: returns nil -- ID as follows: -- COUNTER01 - User defined counter 1 -- COUNTER02 - User defined counter 2 -- COUNTER03 - User defined counter 3 -- COUNTER04 - User defined counter 4 -- COUNTER05 - User defined counter 5 -- COUNTER06 - User defined counter 6 -- ENGINENO - the value in the Engine Number spinner on the Program Control window. -- -- The next counter is used by the program and probably should not be changed by your script -- although your script can "GetCounter(ELAPSEDTIME)" it: -- ELAPSEDTIME - the run time of your script in seconds. Starts at zero when the [Run Script] -- button is pushed. -- -- The next three counters appear on the Tags Detected window: -- TAGCNT - the number of tags detected by the tag detection readers during this run -- UNIQTAG - the number of unique tags ever detected by the tag detection readers -- TAGSUB - the number of UID only tags detected that were replaced by tag data from previous tags -- -- Use this: require("defines") to define these ID's. SetLED(LED#, State, Caption); -- There are four LED on the left edge of the Program Control -- window (1 thru 4), "State" is "clRed" to set the LED to red, "clGreen" to set the LED to -- green or "clYellow" to set the LED to yellow. Use "clClear" to make the LED disappear. -- Caption (in quotes) is optional and if given, -- sets the text under the LED to that value. There is only -- room for 6-8 characters under the LED. Title(textvalue, EngNo); -- sets the title bar text on the Program Control window and optionally, -- the engine number in the spinner on the Program Control window. If the engine number -- is not given, the value in the spinner is not changed. If the textvalue is an empty string, -- the title on the Program Control window is not changed. -- examples Title(title .. " - " .. GetName(EngineNo)); Title(title .. " - " .. GetName(EngineNo), EngineNo); -- You can read back the Engine number from the spinner with: var = GetCounter(ENGINENO); |
Functions that start up or shutdown engines"when" is the time in seconds in the future to execute this command. Set it to zero if you want the function to execute immediately. GetTIUNo(Engine); -- returns the TIU Number that is associatd with the given Engine number the last time the -- engine was seen by the [READ] button on the RTC Main Window. -- Engine number must be between 1 and 99 -- usage: MyEngineNo = Engine; MyTIUNo = GetTIUNo(MyEngineNo); -- Use the returned TIU Number in the following commands. First, you need to know how to stop all engines if something goes wrong: EmergencyStop(message, beep); -- Displays optional message if present. Then sends an emergency stop command to all TIU -- Always returns false. Causes all TIU to remove power from the track. -- Displays optional message if given. Displays message in red. -- Second parameter is also optional, set to true to get a "CRITICAL_STOP" beep when stopping. -- Usually after this function, you must exit RTC, power down the TIU and then start everything again. -- Use only as the parameter for a return statement as: return EmergencyStop(); -- example: if (test) then -- Send Emergency Stop to all TIU return EmergencyStop("Emergency Stop - Engine " .. Engine .. " did not stop on command"); end The next four functions startup/shutdown the engine/lashup after a delay of "when" seconds. The Operations window is NOT displayed. Unless otherwise specified, these functions return true on success and nil on error. Note that if "when" is greater than zero, a true return value only indicates that the command was successfully added to the Dispatch List for future execution. StartUp(when, EngineNo, TIUNo); -- simple starting up of an engine. Use EngineStartUp() to get an Operations Window ShutDown(when, EngineNo, TIUNo); -- simple shutting down of an engine. Use EngineShutDown() to close an Operations Window StartUp(when, LashUpNo, LashUpEngineList, TIUNo); -- simple starting up of a Lashup. Use LashUpStartUp() to get an Operations Window ShutDown(when, LashUpNo, TIUNo); -- simple shutting down of a Lashup. Use LashUpShutDown() to close an Operations Window -- You might want to do a Feature Reset when you shutdown a lashup. Something like: for i,v in ipairs(LashUpEngineList) do
print(string.format("Feature Reset - Engine %d", v)); FeatureReset(math.abs(v), MyTIUNo); end The next four functions startup/shutdown the engine/lashup and open/close the Operations Window for the given engine or lashup. These functions take effect immediately and cannot be scheduled for the future. The effect of having the Operation window displayed is that if you call any function (like Bell(), Whistle(), etc) that has buttons or switches on the window, those buttons or switches will move just as if someone manually activated the button or switch. You can also manually control the engines (but that might mess up the script) -- EngineNo is between 1-99 -- LashUpName is a 16 character name for the Lashup -- LashUpNo is between 101-120 -- LashUpEngineList : a table containing numeric values for Lead EngineNo, -- Middle EngineNo's (if any), Trailing EngineNo. Add 128 to number if the -- engine is reverse running. usage: LashUpEngineList = {3,32,21,4+128}; -- where engine 3 is the lead engine, engines 32 and 21 are middle engines and -- engine 4 (running in reverse) is the trailing engine. -- -- Return nil on error -- Returns true on sucess EngineStartUp(EngineNo, TIUNo); -- Starts a single engine with an Operations window EngineShutDown(EngineNo, TIUNo); -- Shutsdown a single engine and closes the Operations window LashUpStartUp(LashUpName, LashUpNo, LashUpEngineList, TIUNo); -- Starts a lashup with an Operations window LashUpShutDown(LashUpNo, TIUNo); -- Shutsdown a lashup and closes the Operations window |
Engine Reset FunctionsFeatureReset(EngineNo, TIUNo); -- sends a feature reset command to the engine. FactoryReset(EngineNo, TIUNo); -- sends a factory reset command to the engine. |
Functions that control enginesIf "when" is greater than 0 seconds, a "true" return only means that the command was successfully queued for future execution. Commands that are scheduled for future execution never actually return the results of the command itself. GetTIUNo(EngineNo); -- returns the TIU Number that is associatd with the given Engine number the last time the -- engine was seen by the [READ] button on the RTC Main Window. -- Engine number must be between 1 and 99 -- Use the returned TIU Number in the following commands. Setting(iSet, when, EngineNo/LashUpNo, TIUNo); -- sends the "ab" command with value iSet Schedule(Command, when, EngineNo/LashUpNo, TIUNo); -- sends any Command passed to it. For example "u2", "w4", "s010", etc. Whistle(bState, when, EngineNo/LashUpNo, TIUNo); -- Turns the whistle ON or OFF Bell(bState, when, EngineNo/LashUpNo, TIUNo); -- Turns the bell ON or OFF Markers(bState, when, EngineNo/LashUpNo, TIUNo); -- Turns the marker lights ON or OFF Beacon(bState, when, EngineNo/LashUpNo, TIUNo); -- Turns the beacon ON or OFF CabChat(bState, when, EngineNo/LashUpNo, TIUNo); -- Turns the cab chat ON or OFF Smoke(bState, when, EngineNo/LashUpNo, TIUNo); -- Turns the smoke ON or OFF ChuffRate(iRate, when, EngineNo/LashUpNo, TIUNo); -- Sets the chuff rate Rate(iAcc, iDec, when, EngineNo/LashUpNo, TIUNo); -- sets the Acceleration and Decelleration -- rates of the engine. Rates can be from 1 to 25 Smph/sec. Value of 0 for either means no change. GetRate(EngineNo/LashUpNo, TIUNo); -- returns two values, the Acceleration and Decelleration -- Rates of the engine. -- usage: Acc, Dec = GetRate(Eng, TIU); OpenCoupler(bCoupler, when, EngineNo/LashUpNo, TIUNo); -- Opens the front or rear coupler -- bCoupler is FRONT or REAR SetDirection(bDIR, when, EngineNo/LashUpNo, TIUNo); -- Sets the direction of the engine -- bDIR is FORWARD or REVERSE GetDirection(Engine, TIUNo); -- return the direction as an boolean, -- true = FORWARD, false = REVERSE Throttle(iMPH, when, EngineNo/LashUpNo, TIUNo); -- Sends only a speed command unless the Operations Window is displayed for the engine. -- Returns iMPH as an integer on success SetSpeed(iMPH, when, EngineNo/LashUpNo, TIUNo); -- Changes the speed with all of the appropriate bells and whistles (like SFS and SRS) if AutoBell and/or -- AutoWhistle is true (see the next item in this list). -- Returns iMPH as an integer on success AutoBell = true; AutoWhistle = true; AutoBell = false; AutoWhistle = false; -- Put these statements at the beginning of your script if you want to force the SetSpeed() command to play all -- appropriate bells and whistles when start an engine moving or when you stop it. This setting will -- override other AutoBell/AutoWhistle settings. If this statement is not present, the -- "Auto Bell" and "Auto Whistle" values from Setup and the "Auto Bell" and "Auto Whistle" values from -- the Operations Window will be used. -- If all of these setting are false, RTC will just play the standard sounds. GetSetSpeed(Engine, TIUNo); -- return the set speed as an integer in Smph -- engine may be accelerating or decelerating to reach this speed GetSpeed(Engine, TIUNo); -- return the instantaneous speed of -- the engine as an integer in Smph PlaySound(iSoundNo, when, EngineNo/LashUpNo, TIUNo); -- Plays the sound given by iSoundNo. The sound number corresponds to the clip -- number as shown by my ADPCM program. Sound(bState, when, EngineNo/LashUpNo, TIUNo); -- Turn the sound ON or OFF. Sets Master Volume to 0 (OFF) or 100 (ON) ProtoWhistle(bState, when, EngineNo/LashUpNo, TIUNo); -- Turns the Proto Whistle ON or OFF SetQuill(iTone, when, EngineNo/LashUpNo, TIUNo); -- sets the quilling whistle tone, iTone has values 0-3 -- Turn on Proto Whistle first, engine must support this feature SmokeWhistle(bState, when, EngineNo/LashUpNo, TIUNo); -- Turns the Smoke Whistle ON or OFF -- engine hardware must support this feature SwingingBell(bState, when, EngineNo/LashUpNo, TIUNo); -- Turns the Swinging Bell ON or OFF -- engine hardwaremust support this feature SetVolume(Chan, Level, when, EngineNo/LashUpNo, TIUNo); -- set the Volume Level(0-100) of the channel given by Chan. GetVolume(Chan, EngineNo, TIUNo); -- returns the volume level (0-100) of the channel given by Chan as an integer. -- To mute the sounds from an engine, first call GetVolume with Chan of MASTER_VOLUME. Save -- the volume, then use SetVolume with Chan of Master_VOLUME and Level of zero. To unmute, -- use SetVolume with Chan of MASTER_VOLUME and for Level, use the saved volume. NOTE: Any command without a "when" parameter may take 1-2 seconds to complete. If you need to "Get" the result of a "Set" for confirmation, you should Sleep() for a few seconds before the "Get" command. |
Functions that control Accessories and SwitchesAccessory(bState, iChan, AIUNo, TIUNo, when); -- Set actual accessory state -- bState is ON or OFF -- iChan is 1 to 10 -- AIUNo is 1 to 5 -- TIUNo is 1 to 5 -- Returns true on success -- On Error: returns nil GetAccStatus(iChan, AIUNo, TIUNo); -- returns the state of the accessory on the -- iChan is 1 to 10 -- AIUNo is 1 to 5 -- TIUNo is 1 to 5 -- return true if the accessory is ON -- returns false if the accessory is OFF GetAccName(iChan, AIUNo, TIUNo); -- returns the name of the accessory on the -- given Channel, AIU and TIU as a string. -- iChan is 1 to 10 -- AIUNo is 1 to 5 -- TIUNo is 1 to 5 -- returns "*" if the AIU or Channel is not defined. VirtualAccessory(bState, iChan, AIUNo, TIUNo); -- Set virtual accessory state - does not send any commands to the TIU. -- Useful to pass information between PC threads. Use GetAccStatus() to read -- the position set by this function. You can set Virtual Accessories even in TIU that don't exist. -- bState is ON or OFF -- iChan is 1 to 10 -- AIUNo is 1 to 5 -- TIUNo is 1 to 5 -- Returns true on success -- On Error: returns nil Switch(bState, iChan, AIUNo, TIUNo, when); -- Set acutal switch state -- bState as described above, ie : NORMAL or REVERSE -- iChan is 1 to 10 -- AIUNo is 1 to 5 -- TIUNo is 1 to 5 -- Returns true on success -- On Error: returns nil GetSwitchPositionNames(); -- returns two values, both strings containing the words used -- to indicate the position of switches. -- For example "Straight"/"Diverge", "Closed"/"Open", "Normal"/"Reverse". -- The default values used in the program are "Normal"/"Reverse". -- usage: ClosedName, OpenName = GetSwitchPositionNames(); GetSwitchStatus(iChan, AIUNo, TIUNo); -- returns the state of the switch on the -- iChan is 1 to 10 -- AIUNo is 1 to 5 -- TIUNo is 1 to 5 -- returns true if the switch is in the Normal position -- return false if the swtich is in the Reverse position GetSwitchName(iChan, AIUNo, TIUNo); -- returns the name of the switch on the -- given Channel, AIU and TIU as a string. -- iChan is 1 to 10 -- AIUNo is 1 to 5 -- TIUNo is 1 to 5 -- returns "*" if the AIU or Channel is not defined. Siding(bState, iChan, AIUNo, TIUNo, when); -- Set siding power by activating an AIU Accessory channel and then sending a Watchdog signal -- to the tracks powered by the given TIUNo. -- This command is just like the Accessory() command except that it sends a Watchdog signal after the -- AIU channel is turned ON. Look at the documentation for the Watchdog() command for more details. -- bState is ON or OFF -- iChan is 1 to 10 -- AIUNo is 1 to 5 -- TIUNo is 1 to 5 -- Returns true on success -- On Error: returns nil Watchdog(TIUNo); -- Sends a Watchdog signal to the tracks powered by the given TIUNo. -- All engines found on the powered tracks are set active in the Engine List. -- Use this command immediately after a siding is powered on in order to put the engines -- into Command Mode. TIUActive{TIUNo); -- returns true if the given TIU is active, otherwise returns false; -- returns nil on error |
Functions that present layout or engine status-- Program Control window was started. This function will return the same value across -- all PC windows if called at the same time. -- As a "number", it is floating point and should never be compared for -- equality (except to 0.0). If you need to do that, convert the number to an integer -- with: math.floor(RunTime() + 0.5). When used with string.format() remember to -- use a floating point specifier like "%.2f". -- RunTime can be selected on the Debug window to timestamp each debugging message displayed -- to that window. You can use RunTime() to timestamp each message to the Program Control Messages -- window and then be able to matchup the messages in the two windows. -- -- Note that RunTime is not the same as ElapsedTime (returned by GetCounter(ELAPSEDTIME)). -- ElapsedTime is the time in seconds since the [Run Script] button was pressed and will -- be different for each Program Control window. MicroSeconds(); -- returns the value of the Windows Performance Counter in microseconds -- as an integer. -- This value does not have an absolute meaning. Always use the difference between -- two calls as an elapsed time. TIUVersion(TIUNo); -- return value is the version number of the given TIU as a number -- typical values 5.3, 6.01, etc. -- returns 0.0 if the TIU is not connected CommStatus(); -- return 3 values giving the status of the two ports (USB Serial and WiFi) -- used by the RTC program. Returns true or false. -- usage: RadioPortConnected, TIUConnected, RFIDPortConnected = CommStatus(); GetTagReaders(); -- returns 2 values giving the number of Tag Detectors and the -- number of Tag Readers connected. The Tag Reader count is only correct when the tag -- detectors answer up with their initial packets. That is only when they are powered -- on and waiting for mDNS identification when RTC is started. The Tag Detector count -- should be always correct. -- usage: NumDetectors, NumReaders = GetTagReaders(); RAM(address, Engine, TIUNo); -- returns the byte at the RAM address given as address. -- The first 1024 bytes (address 0x000 to 0x3FF) can be read. -- On error : Returns nil -- You can download my documentation for this function here - Engine RAM Mapping.pdf. RAM2(address, Engine, TIUNo); -- returns the 2 bytes at the RAM address given as address. -- The first 1024 bytes (address 0x000 to 0x3FF) can be read. -- On error : Returns nil -- You can download my documentation for this function here - Engine RAM Mapping.pdf. RAM4(address, Engine, TIUNo); -- returns the 4 bytes at the RAM address given as address. -- The first 1024 bytes (address 0x000 to 0x3FF) can be read. -- On error : Returns nil -- You can download my documentation for this function here - Engine RAM Mapping.pdf. DTO(Engine, TIUNo); -- returns the Engine's trip odometer mileage (DTO) as a number -- On error : Returns nil DOD(Engine, TIUNo); -- returns the Engine's odometer mileage (DOD) as a number -- On error : Returns nil DCH(Engine, TIUNo); -- returns the Engine's chronometer in seconds (DCH) as a number -- On error : Returns nil PCNo(); -- returns the number of the Program Control window running this script -- A number from 1 to 10 as an integer Info(Engine); -- under development -- the more I look at this, the less useful it appears -- Returns information about the engine, returns 6 values -- 1. Engine Type as an Integer (valid only if you have called GetSpeed() previously) -- 2. Scaling Factor as a Number (valid only if you have called DTO() or DOD() previously) -- 3. Last Sent Speed Value as an Integer (valid only if you have called SetSpeed() or Throttle() previously) -- 4. Motion Sound Request as a Boolean -- 5. Auto Bell setting as a Boolean -- 6. Auto Whistle setting as a Boolean -- Returns nil on error Ping(Engine, TIUNo); -- Sends 100 packets to the Engine and returns the number that -- were acknowledged by the Engine. Returns an integer between 0 and 100. -- On error : Returns nil. |
Functions that make script writing easier-- a float and and has a 100 nanosecond resoultion. eg: 1, 1.0, 3.545363, 0.1, 0.1111. -- Returns true if sleep succeeded, false if sleep was terminated early, nil on error -- Calling Sleep() allows the User Interface to remain responsive. -- Any commands which are queued up for execution will run as -- scheduled while the script is sleeping. -- Pressing the [STOP] button will terminate a sleep in progress but will not stop -- queued up commands. Yield(); -- when the script cannot call Sleep() because it must run as fast as possible, -- call the Yield() function inside of loops. This will enable the [STOP] button and function -- buttons to have an effect. Otherwise, the loop will run so fast that nothing will -- be able to break into it. -- Normally returns true, returns false on termination event or error Beep(sound); -- play alert or notification sounds (you must have each sound enabled -- on your computer or the sounds won't play). Sounds are defined in "defines.lua" and are: -- Beep(); play default beep sound -- Beep(DEFAULT_BEEP); play default beep sound -- Beep(CRITICAL_STOP); play critical stop sound -- Beep(QUESTION); play question sound -- Beep(EXCLAMATION); play exclamation sound -- Beep(ASTERISK); play asterisk sound -- returns true on success, false on failure ClearDispatchList(); -- Clears any pending commands in the dispatch list. Useful if -- you are exiting on an error condition. Returns true if the Dispatch List processor was -- running and was cleared. Returns false if the Dispatch List processor was not running. CountDispatchList(); -- returns the number of entries in the dispatch list. Useful if you want -- to wait for the dispatch list to empty (count == 0) on an error condition. Returns false if the -- dispatch list is not running InputBox(Caption, Prompt, Default); -- Displays an input box using Caption and Prompt. Input -- area is set to the value in Default. The user can enter a new value and press [OK] or -- press [Cancel] to return the default value. Returns the result as a string. -- Returns nil if the parameters are invalid. -- Use the InputBox function when there is a default value that should be used when the user -- chooses the [Cancel] button (or presses [Esc]) to exit the dialog. If the script needs to -- know whether the user chooses [OK] or [Cancel], use the InputQuery() function instead. InputQuery(Caption, Prompt, Default); -- Displays an query box using Caption and Prompt. Input -- area is set to the value in Default. The user can enter a new value and press [OK] which -- returns the new value as a string. Pressing [Cancel] or [Esc] causes the function to -- return false. Returns nil if the parameters are invalid. -- If the default value should be used when the user cancels out of the dialog, use InputBox(). InputDropDown(Caption, Prompt, Index, DataTable); -- Displays an input box similar in -- operation to the InputQuery() function. Instead of a single input line, a drop down box -- is populated with the strings contained in the DataTable and pointing to the 'Index' entry -- of the table as the default selection. The user can select one of the items in the drop -- down list or enter a new value and press [OK] which returns the value as a string. Pressing -- [Cancel] or [Esc] causes the function to return false. Returns nil if the parameters are invalid. -- The DataTable must be an ordinary array and can contain up to 99 entires. -- Look at the script "InputBox Test.lua" for an example of how to use this function. InputLine(WaitTime); -- Opens a small input box just below the Messages Window. Waits WaitTime seconds -- for the user to type in an input line and returns that line as a string. If WaitTime expires or -- any other error occurs, returns nil. Stop(message, beep); -- Stop the script and run the function cleanup(). -- Use only as the parameter for a return statement as: return Stop(message); -- example: if (test) then return Stop("End of Test"); end; -- Always returns false. Displays optional message if given. Message is displayed in Red. -- Second parameter is also optional, set to true to get a "EXCLAMATION" beep when stopping. -- This is for an immediate stop of the script. Usually you must press the [STOP] button -- to run cleanup() and end the script. EmergencyStop(message, beep); -- see next section Debug(); -- returns the value of the global RTC debug level as set on the debug window. If you -- don't know how to access the debug window, send me an email. An integer between 0 (no debug -- messages) to 9 (full debug messages). -- Remember that you can't do this "if (Debug()) then ...." because in Lua, a value of 0 is true. -- Instead, do this "if (Debug() > 0) then ....". GetDebugCounter(ID); -- returns the value of the debug counters on the main window and the -- setup spinners on the Setup window. Only useful -- for debugging and if you know what each counter or spinner means. Returns nil on failure. -- Here is the list of debug registers: -- ID Radio Comm to TIU WiFi Comm to WTIU -- -- ----------------- ----------------- -- 1 : -- WiFiTO -- 2 : -- Checksum -- 3 : Sent Sent -- 4 : Busy Failure -- 5 : Retries Retries -- 6 : NAK Error -- 7 : CRC Reset -- 8 : Tags Tags -- 9 : DL Entries DL Entries -- 10 : Re-entrant Re-entrant -- 11 : Caught Caught -- 12 : TIU TIU -- 13 : No Response No Response -- 14 : NAK Length Timedout -- 15 : Dispatched Dispatched -- 16 : DL Total DL Total -- 17 : Start Start -- 18 : NF Echo -- Here is the list of "Setup" window spinners -- 1 : Max Speed -- 2 : Radio Comm Port Number -- 3 : RFID Comm Port Number Pretty(Var); -- returns a string which is a reasonable representation of the contents of the -- variable "Var". "Var" can be any Lua type -- This function needs defines.lua to be included with require("defines"); PrettyPrint(Var); -- Calls Pretty(Var) and displays the resultant string in -- Messages window. Same as print(Pretty(Var). -- This function needs defines.lua to be included with require("defines"); PopupMessage(Message, Caption, SecondMessageLine, Delay); -- Displays a Message in a popup window using -- an optional Caption, an optional SecondMessageLine and an optional Delay time in seconds. -- If not given, Delay is set to 2 seconds. This is how long the popup is on the display. -- If not given, SecondMessageLine is blank. -- If not given, Caption is "Information". -- At least one parameter, the Message itself, is required. |
Functions that return information about Engines and LashupsGetTIUNo(EngineNo); -- returns the TIU Number that is associatd with the given Engine number-- Engine number must be between 1 and 99 -- Use the returned TIU Number in any RTC Control Language command. IsEngineRunning(EngineNo); -- returns true if -- the engine was started with StartUpActiveEngines()or EngineStartUp(). Otherwise returns false. -- Engines started with StartUp() are not seen by this function -- On Error: returns nil IsLashUpRunning(LashUpNo); -- returns true if -- the lashup was started with LashUpStartUp(). Otherwise returns false. -- On Error: returns nil Counts(); -- returns two values, the number of active engines and the number of inactive engines -- usage: ActiveCount, InactiveCount = Counts(); -- returns nil,nil on error. Exists(EngineNo); -- returns true if the engine exists, otherwise returns false (engines 1-99) -- returns nil on error IsActive(EngineNo); -- returns true if the engine is active, otherwise returns false (engines 1-99) -- returns nil on error GetName(EngineNo); -- Returns two values, the engine name as a string and the PS -- level (2 or 3) as an integer for EngineNo (engines 1-99) -- example: Name, PS = GetName(Engine); GetEngineName(CarModel); -- In the tag() function, if the EngineNo read from the tag is greater than 0, -- use the CarModel value with this function to get a string containing the model name of the -- engine, eg "SW1500", "GP7", etc. GetCarName(CarModel); -- In the tag() function, if the EngineNo read from the tag is 0, use the CarModel -- value with this function to get a string containing the model name of the car, eg "Boxcar", -- "Coach", etc. GetRailroadName(Railroad); -- In the tag() function, use the Railroad value with this function -- to get a string containing the railroad name contained in the detected tag, eg "P&LE", etc. GetEngineType(Engineype); -- In the tag() function, use the Engine Type value with this function -- to get a string containing the type of engine ("Steam", "Diesel", etc). GetTagLocation(TagLocation); -- In the tag() function, use the Tag Location value with this function -- to get a string containing the tag location contained in the detected tag, eg "Front Truck", etc. GetCouplerLocation(TagLocation); -- In the tag() function, use the Tag Location value with this function -- to get a string containing the the location of the tag on the car/engine, eg "Front and Rear Couplers", etc. |
Send any Command to the TIUCommand(Command, EngineNo, TIUNo); -- Send any command to the TIU or Engine. Does not work with Lashups. Command can be any legal command string such as "s0" (set speed to zero), "y2" (set DCS engine 1 as working engine), etc. EngineNo in the parameter list must be the Engine Number whereas any engine number in a command must be a DCS engine number. Commands to the TIU or AIU still require an Engine Number but it is ignored.Returns 1 value "nil" if the command fails. Returns 3 values if the command succeeds: 1. true as a boolean 2. the number of characters in the response to the command as an integer 3. the response to the command (from the TIU) as a string Useage: Status, CharCount, Response = Command("s0", EngineNo, TIUNo) Very dangerous in the wrong hands. Can be used to send new commands or commands to features that I haven't implemented yet (Trolley commands come to mind). |
Here is a listing
of the Startup_shutdown_engine.lua file
(this may not be as up to date as in the zip file linked to below): -- Semicolons are not required in Lua code |
function NOP(Engine, TIU)Following the function, define the function name and button label:
print("NOP() This script does (almost) nothing");
return true;
end
Function10Name, Function10Label = NOP, "NOP";This line tells the Program Control window to put the phrase "NOP" on the button and when the user presses the button, to call the function NOP(Eng, TIU).
Here is a example listing
of Helper Functions
--[[---------------------------------------------------------------------------------------]] |
-- Counter Labels
Counter01Label = "Engines Detected";
Counter02Label = "State Machine";
BumpCounter(ID); -- Increments one of the counters on the Program Control window by 1Here is an example (fairly useless) script (requires defines.lua):
DecCounter(ID); -- Decrements one of the counters by 1
GetCounter(ID); -- Returns the value in one of the counters
ClearCounter(ID); -- Sets one of the counters to zero.
SetCounter(ID, value); -- sets one of the counters to the value given
Here is a listing
of the CounterLabelTest.lua script file
(this may not be as up to date as the zip file linked to below): -- Semicolons are not required in Lua code |
Here is a listing
of the LED Test.lua script file
(this may not be as up to date as the zip file linked to below): -- Semicolons are not required in Lua code |
Here is a listing
of the Three Engine Demo.lua script file
(this may not be as up to date as the zip file linked to below): -- Semicolons are not required in Lua code |
DETECTENGINES In this state, we are just trying to detect all of the running engines and ascertain their order on the layout. As each tag on a front engine truck is detected, toot the horn. This just signals to me that the script is processing tags. We care about tags on an engine, on the front truck and detected by Detector 1. As each engine is detected, I record the order (EngineDetectedOrder). When I detect the first engine again, all of the engines have gone all away around the layout once. The script increases the speed of the three engines to 15, 18, & 20 Smph respectively. Now I should have detected 3 engines. If not, the script displays a message and goes into the IDLE state. If so, the script goes into READYGO_3 state. (Now, having written this description, I see that I should have more checking for the case where more than 3 engines are detected. I'll leave that has an exercise for you.) |
READYGO_3, READYGO_3a, & READYGO_3b These three states are basically identical so I will write one description. As each tag is detected, the tag() function is called and the state machine value (SM) will cause one of these states to execute. Tags from other non-engine cars are ignored. Knowing now the order of the three engines, the script will run the engines until each passes over one of the three detectors (1, 2 & 3). Then it stops the engine. When all three engines are stopped (at a detector), the script pauses for a few seconds and then the script starts the engines moving again at different random speeds. The three states detect the three engines over the three detectors. The state machine (SM) is set to the next of these states and waits for each of the three engines to run up to the next stopping point (at the next detector). So the state machine runs these three states in sequence over and over until the [STOP] button is pushed. |
READYGO_3, READYGO_3a, & READYGO_3b These three states are basically identical so I will write one description. As each tag is detected, the tag() function is called and the state machine value (SM) will cause one of these states to execute. Tags from other non-engine cars are ignored. Knowing now the order of the three engines, the script will run the engines until each passes over one of the three detectors (1, 2 & 3). Then it stops the engine. When all three engines are stopped (at a detector), the script pauses for a few seconds and then the script starts the engines moving again at different random speeds. The three states detect the three engines over the three detectors. The state machine (SM) is set to the next of these states and waits for each of the three engines to run up to the next stopping point (at the next detector). So the state machine runs these three states in sequence over and over until the [STOP] button is pushed. |
Here is a listing
of the defines.lua script file
(this may not be as up to date as the zip file linked to below): --[[ |
Here is a listing
of the functions.lua script file
(this may not be as up to date as the zip file linked to below): --[[ |
Here is a listing
of the Lashup_startup_shutdown.lua script
(this may not be as up to date as the zip file linked to below): -- Semicolons are not required in Lua code |
luac53 -o script.out script.lua
Function that Generates EventsSetEvent(DestPCNo, Type, P1, P2, P3); -- passes the four integer parameters to the Program Control window -- specified by DestPCNo. As of RTC v4.4.2, there can be 10 PC windows numbered 1 through 10. The -- maximum may be raised in the future. In order for the destination PC window to receive the event, -- the script running in that window must have a function declared as "event(FromPCNo, Type, P1, P2, P3)". -- The receiving script will execute the event() function, presenting the source PC window number -- along with the four parameter values. The parameters are lua integers which are defined as -- 64 bit signed values. The meaning and contents of these parameters are not fixed and can be -- whatever you wish. Type/P1/P2/P3 are optional and if not given, zero is passed. -- The meaning of Type, P1, P2 and P3 must be agreed to by both the sender and receiver -- of the event. An example of Event Type values can be seen in the script distances.lua. -- The function returns nil if the destination PC window does not exist -- otherwise returns true when the event is sent. The function does NOT wait for the event -- to complete. |
✅event(FromPCNo, Type, P1, P2, P3) -- is called by any PC window using the SetEvent() function. It is used for commnications between the PC windows. Return true on success, false on failure. "FromPCNo" is the Program Control window # of the script which executed the SetEvent() function. Type, P1, P2 & P3 are integers which are being sent along with the event. Look at the SetEvent() description above for details of how this function is used. If the function does not appear in your script, RTC does not attempt to call it. Any PC window can send an event to itself. |
Here is a listing
of the event.lua script
(this may not be as up to date as the zip file linked to below): -- Semicolons are not required in Lua code |
When you run the event.lua script in both PC window #1 and #2, here is what PC window #1 should look like after successful completion. | |
Here is what PC window #2 should look like. |
Functions that access the Global Integers and StringsSetGlobalInt(index, value); GetGlobalInt(index); TrySetGLobalInt(index, value); TryMultiSetGlobalInt(table, value); -- There are 100 global integer locations where any Program Control window -- can store or retrieve a value. This locations are useful for communications between Program -- control windows. Index is 1-100, value is any integer. Use -- SetGlobalInt() to store a value and GetGlobalInt() to retrieve a value. -- TrySetGlobalInt() attempts to set a value into a global integer and will only succeed if the -- global integer is zero or equal to value. This test and set is done atomically. If it succeeds -- it will return true, if it can't get the lock, it will return false. -- TryMultiSetGlobalInt() attemps to set a value into several global integers and will only succeed -- if all of the global integers are zero or equal to value. This test and set is done atomically. -- It returns two values. The first is true if it gets a lock on all of global integers, false -- if it cannot. The second value returned is the index number of the global integer which would -- not lock otherwise returns zero. table is a lua table, for example : {4, 22, 12} SetGlobalString(index, string); GetGlobalString(index); -- There are 100 global string locations where any Program Control window -- can store or retrieve a string. These locations are useful for communications between Program -- Control windows. Index is 1-100, string is a string. Use SetGlobalString() to store a string -- and GetGlobalString() to retrieve a string. |
Here is a listing
of the Global Variables Test.lua script
(this may not be as up to date as the zip file linked to below): -- Semicolons are not required in Lua code |
When you run the Global Variables test, this is what the PC window should look like. This test repeats over and over until you press [STOP]. |
email : markd@silogic.com
The DiVecchio
genealogy home page
The Frazzini
genealogy home page
This site will be under construction for a while.