forked from kmarkus/rFSM
-
Notifications
You must be signed in to change notification settings - Fork 2
/
rfsm_await.lua
108 lines (90 loc) · 3.14 KB
/
rfsm_await.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
-- rFSM await extension.
-- Trigger on events received in different steps.
--
-- (C) 2010-2013 Markus Klotzbuecher <[email protected]>
-- (C) 2014-2020 Markus Klotzbuecher <[email protected]>
--
-- SPDX-License-Identifier: BSD-3-Clause
--
-- syntax: events={ "await('e_foo', 'e_bar')" } will trigger when both
-- e_foo and e_bar have been received.
--
-- Note that await is internally transformed to separate events in the
-- events table plus a guard condition.
--
local rfsm = require "rfsm"
local utils = require "utils"
local string, print, ipairs, pairs = string, print, ipairs, pairs
local get_sta_mode = rfsm.get_sta_mode
module("rfsm_await")
--- Pre-process await and setup handlers.
-- @param fsm initalized root fsm.
local function expand_await(fsm)
fsm.info("rfsm_await: await extension loaded")
--- check and parse an await spec.
-- @returns a table of await events or false if event is not an
-- an await
local function parse_await(event)
local awaitspec = string.match(event, "await%((.*)%)")
if not awaitspec then return false end
awaitspec = string.gsub(awaitspec, "['\"]", "") -- remove ["']
local evlist = utils.split(awaitspec, ",") --
return utils.map(utils.trim, evlist) -- trim whitespace
end
--- Generate await handlers.
-- Generate update, reset and guard functions.
local function gen_await_handlers(await_events, tr)
local etab={}
local aw_src_sta=tr.src -- caching this is OK in terms of
-- dynamic FSM changes, since if this
-- state is replaced, then its
-- transitions will need an update too.
local function reset()
fsm.dbg("AWAIT", "reset await monitoring")
for _,e in ipairs(await_events) do etab[e]=false end
end
-- make sure that only await_events get set
local function update(fsm, events)
if get_sta_mode(aw_src_sta) == 'inactive' then return end
for _,e in ipairs(events) do
if etab[e]~=nil and etab[e]==false then
etab[e]=true
fsm.dbg("AWAIT", "update received:", e)
end
end
end
local function _cond(events)
for e,v in pairs(etab) do
if not v then return false end
end
return true
end
local function cond(events)
local res = _cond(events)
fsm.dbg("AWAIT", "checking await condition:", res)
return res
end
reset()
return update, reset, cond
end
rfsm.mapfsm(function (tr, p)
if not tr.events then return end
for i=1,#tr.events do
local await_events = parse_await(tr.events[i])
if await_events then
fsm.dbg("AWAIT", "matched await spec " .. tr.events[i])
local update, reset, cond = gen_await_handlers(await_events, tr)
for _,e in ipairs(await_events) do tr.events[#tr.events+1] = e end
rfsm.pre_step_hook_add(fsm, update) -- update prior to each step
tr.src.exit = utils.advise('after', tr.src.exit, reset) -- reset on src state exit
if tr.guard then
old_guard = tr.guard
tr.guard=function(...) return cond(...) and old_guard(...) end
else
tr.guard = cond
end
end
end
end, fsm, rfsm.is_trans)
end
rfsm.preproc[#rfsm.preproc+1] = expand_await