feat(hsm): first version of hsm library functional
This commit is contained in:
12 files changed
+1371
-46
No files matched your search
@@ -0,0 +1,109 @@
|
||||
require "lib.hsm"
|
||||
|
||||
-- Example machine definition: Stoplight
|
||||
stoplight_def = {
|
||||
default: "Red" -- Initial substate is "Red"
|
||||
|
||||
-- Global timer: every 15s, transition into FlashingRed
|
||||
"FlashingRed every 15"
|
||||
|
||||
-- Terminate the HSM after 25s
|
||||
"ExitHSM after 25"
|
||||
|
||||
-- keyboard events
|
||||
"Green on G_PRESSED"
|
||||
"Red on R_PRESSED"
|
||||
"Yellow on Y_PRESSED"
|
||||
"ExitHSM on Q_PRESSED"
|
||||
"FlashingRed on F_PRESSED"
|
||||
|
||||
Red: {
|
||||
-- After 5s in Red, go to Green
|
||||
"Green after 5"
|
||||
}
|
||||
|
||||
Green: {
|
||||
-- After 5s in Green, go to Yellow
|
||||
"Yellow after 5"
|
||||
}
|
||||
|
||||
Yellow: {
|
||||
-- After 1s in Yellow, return to Red
|
||||
"Red after 1"
|
||||
}
|
||||
|
||||
FlashingRed: {
|
||||
-- On entering FlashingRed, immediately go to FlashingRedOn
|
||||
default: "FlashingRedOn"
|
||||
|
||||
-- After 5s in FlashingRed, transition back to Red
|
||||
"Red after 5"
|
||||
|
||||
FlashingRedOn: {
|
||||
-- After 0.5s in FlashingRedOn, toggle to FlashingRedOff
|
||||
"FlashingRedOff after 0.5"
|
||||
}
|
||||
|
||||
FlashingRedOff: {
|
||||
-- After 0.5s in FlashingRedOff, toggle back to FlashingRedOn
|
||||
"FlashingRedOn after 0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-- Define a Stoplight class that extends the HSM runtime.
|
||||
class Stoplight extends create_hsm(stoplight_def)
|
||||
-- HSM-level actions.
|
||||
onHSMEntry: => print "stoplight started"
|
||||
onHSMUpdate: (dt) =>
|
||||
onHSMExit: => print "stoplight stopped"
|
||||
|
||||
-- Red state actions.
|
||||
onRedEntry: => print "red light ON"
|
||||
onRedUpdate: (dt) =>
|
||||
onRedExit: => print "red light OFF"
|
||||
|
||||
-- FlashingRed state actions.
|
||||
onFlashingRedEntry: =>
|
||||
onFlashingRedUpdate: (dt) =>
|
||||
onFlashingRedExit: =>
|
||||
|
||||
-- FlashingRedOn state actions.
|
||||
onFlashingRedOnEntry: => print "red light ON"
|
||||
onFlashingRedOnUpdate: (dt) =>
|
||||
onFlashingRedOnExit: => print "red light OFF"
|
||||
|
||||
-- FlashingRedOff state actions.
|
||||
onFlashingRedOffEntry: =>
|
||||
onFlashingRedOffUpdate: (dt) =>
|
||||
onFlashingRedOffExit: =>
|
||||
|
||||
-- Yellow state actions.
|
||||
onYellowEntry: => print "yellow light ON"
|
||||
onYellowUpdate: (dt) =>
|
||||
onYellowExit: => print "yellow light OFF"
|
||||
|
||||
-- Green state actions.
|
||||
onGreenEntry: => print "green light ON"
|
||||
onGreenUpdate: (dt) =>
|
||||
onGreenExit: => print "green light OFF"
|
||||
|
||||
-- Instantiate the stoplight HSM.
|
||||
stoplight = Stoplight!
|
||||
|
||||
-- Set up non-blocking input.
|
||||
getch = require "lua-getch"
|
||||
getch.set_raw_mode io.stdin
|
||||
getch.set_nonblocking io.stdin, true
|
||||
|
||||
-- Main loop parameters.
|
||||
dt = 0.05
|
||||
while stoplight\update dt
|
||||
key = getch.get_char io.stdin
|
||||
switch key
|
||||
when 102 then stoplight\trigger "F_PRESSED"
|
||||
when 103 then stoplight\trigger "G_PRESSED"
|
||||
when 113 then stoplight\trigger "Q_PRESSED"
|
||||
when 114 then stoplight\trigger "R_PRESSED"
|
||||
when 121 then stoplight\trigger "Y_PRESSED"
|
||||
os.execute "sleep #{dt}"
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
-- Recursively prints the contents of a table in a human-readable format.
|
||||
--
|
||||
-- This function traverses the provided table `tbl` and prints each key-value
|
||||
-- pair to the standard output. For nested tables, it calls itself recursively,
|
||||
-- increasing the indentation level to visually represent the table hierarchy.
|
||||
--
|
||||
-- @param tbl table: The table whose contents are to be printed.
|
||||
-- @param indent number: (Optional) The current indentation level. Defaults to 0.
|
||||
-- @return nil
|
||||
export print_table = (tbl, indent) ->
|
||||
-- Use the “?=” operator to give a default value.
|
||||
unless indent
|
||||
indent = 0
|
||||
-- Omit the extra “do” after the loop header.
|
||||
for k, v in pairs tbl
|
||||
formatting = string.rep(" ", indent) .. k .. ": "
|
||||
if type(v) == "table"
|
||||
print formatting
|
||||
print_table v, indent + 1
|
||||
elseif type(v) == "boolean"
|
||||
print formatting .. tostring(v)
|
||||
else
|
||||
print formatting .. v
|
||||
@@ -1,41 +0,0 @@
|
||||
require "src.util.string"
|
||||
require "src.util.validators"
|
||||
|
||||
---
|
||||
-- parse_transition
|
||||
-- Parses a transition string into a table with:
|
||||
-- • target: the target state name,
|
||||
-- • type: "after" | "every" | "on",
|
||||
-- • time: number (if timer-based),
|
||||
-- • event: string (if event-based).
|
||||
export parse_transition = (trans) ->
|
||||
unless type(trans) == "string"
|
||||
error "Transition must be a string"
|
||||
|
||||
words = split trans
|
||||
unless #words == 3
|
||||
error "Transition must consist of three words"
|
||||
|
||||
unless is_valid_symbol words[1]
|
||||
error "Invalid state name"
|
||||
|
||||
switch words[2]
|
||||
when "on"
|
||||
unless is_valid_symbol words[3]
|
||||
error "Invalid event name"
|
||||
{target: words[1], event: words[3]}
|
||||
|
||||
when "after"
|
||||
num = tonumber words[3]
|
||||
unless is_valid_timer num
|
||||
error "Invalid time value in 'after' transition type"
|
||||
{target: words[1], after: num}
|
||||
|
||||
when "every"
|
||||
num = tonumber words[3]
|
||||
unless is_valid_timer num
|
||||
error "Invalid time value in 'every' transition type"
|
||||
{target: words[1], every: num}
|
||||
|
||||
else
|
||||
error "Unknown transition type"
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
-- Splits the input string into an array of non-whitespace substrings.
|
||||
--
|
||||
-- This function uses Lua's pattern matching to iterate over the provided
|
||||
-- string `s` and collects each contiguous sequence of non-whitespace
|
||||
-- characters into a new table. If the input is not a string, the function
|
||||
-- raises an error.
|
||||
--
|
||||
-- @param s string: The string to be split. Must be a valid string.
|
||||
-- @return table: An array of substrings obtained by splitting `s` on whitespace.
|
||||
-- @raise error if the input parameter `s` is not a string.
|
||||
export split = (s) ->
|
||||
unless type(s) == "string"
|
||||
error "split() requires a string parameter"
|
||||
chunks = {}
|
||||
for substr in string.gmatch s, '%S+'
|
||||
table.insert chunks, substr
|
||||
chunks
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
-- Determines if the provided timer value is valid.
|
||||
--
|
||||
-- A valid timer is defined as a number greater than zero.
|
||||
--
|
||||
-- @param x number: The timer value to validate.
|
||||
-- @return boolean: Returns `true` if `x` is a positive number; otherwise, returns `false` or `nil`.
|
||||
export is_valid_timer = (x) ->
|
||||
x and x > 0
|
||||
|
||||
---
|
||||
-- Checks whether the given string is a valid symbol.
|
||||
--
|
||||
-- A valid symbol is defined as a non-empty string that contains only
|
||||
-- alphabetic characters (A-Z and a-z). If the input is not a string,
|
||||
-- the function returns `false`.
|
||||
--
|
||||
-- @param s string: The string to be validated as a symbol.
|
||||
-- @return boolean: Returns `true` if `s` contains only letters; otherwise, returns `false`.
|
||||
export is_valid_symbol = (s) ->
|
||||
-- Return false immediately if s is not a string.
|
||||
unless type(s) == "string"
|
||||
return false
|
||||
-- Use Lua's pattern matching to ensure the string only contains letters.
|
||||
if string.match s, "^[A-Za-z]+$"
|
||||
true
|
||||
else
|
||||
false
|
||||
Reference in new issue
Block a user