PROBLEM DESCRIPTION
The following code is a minimal example that replicates a problem I am having. The rFSM library is used, which can be found here: https://github.com/kmarkus/rFSM
There are three lua scripts. The "main", called runscript.lua, which initializes and runs the state machine (SM) that needs to be run, called mainTask.lua, which in turn, uses an auxiliary state machine subTask.lua.
The code flow is the following. We start inside the state machine mainTask.lua and immediately enter the Sim composite state, and then the state evaluate. There, we are calling the function evaluateData which is inside subTask.lua and set the local variable var to false. After this function call, we go to mainTask.lua again, make a transition and end up inside subTask.lua again. There we print the value of var.
Again, schematically: mainTask.lua -> subTask.lua -> mainTask.lua -> subTask.lua.
We expect the value of var to be false. However, as you can see, inside mainTask.lua, we are loading the subTask.lua twice via rfsm.load("subTask.lua"),. This results in having two instances of the auxiliary SM, but I want to have one instead.
The rfsm.load function is
function load(file)
local fsm = dofile(file)
if not is_state(fsm) then
error("rfsm.load: no valid rfsm in file '" .. tostring(file) .. "' found.")
end
return fsm
end
CODE
runscript.lua
-- Set lua path to location of rFSM library
package.path = "/home/anfr/rFSM-master/?.lua;./?.lua";
require 'rfsm';
print('[runscript.lua] Before rfsm.load("mainTask.lua")')
fsm_ = rfsm.load("mainTask.lua")
print('[runscript.lua] Before rfsm.init(fsm_)')
fsm = rfsm.init(fsm_);
print('[runscript.lua] Before rfsm.run(fsm)')
rfsm.run(fsm);
mainTask.lua
local var = true;
return rfsm.state{
Sim = rfsm.composite_state
{
evaluate = rfsm.state {
entry=function()
var = false;
if (evaluateData(var)) then
rfsm.send_events(fsm, "internal_EvaluateDone")
end
end,
},
sub = rfsm.load("subTask.lua"),
rfsm.trans {
src='initial',
tgt='evaluate',
effect=function()
print('[mainTask.lua] initial -> evaluate')
end,
},
rfsm.trans {
src='evaluate',
tgt='sub',
events={'internal_EvaluateDone'},
effect=function()
print('[mainTask.lua] evaluate -> sub with event "internal_EvaluateDone"')
end,
},
}, -- end of Sim
Seq = rfsm.composite_state
{
entry=function()
print('Entry Seq')
end,
exit=function()
print('Exit Seq')
end,
Running = rfsm.composite_state
{
entry=function()
print('Entry Running')
end,
exit=function()
print('Exit Running')
end,
--sub_2 = sub,
sub_2 = rfsm.load("subTask.lua"),
rfsm.trans {
src='initial',
tgt='sub_2',
effect=function()
print('[Seq composite state] initial -> sub_2')
end,
},
},
rfsm.trans {
src='initial',
tgt='Running',
effect=function()
print('[Seq composite state] initial -> Running')
end,
},
}, -- end of Seq
rfsm.trans {
src='initial',
tgt='Sim',
effect=function()
print('[mainTask.lua] initial -> Sim')
end,
},
};
subTask.lua
print("--- start of subTask.lua ---")
local var = true;
function evaluateData(value)
print("[subTask.lua] var before setting in evaluateData is " .. tostring(var));
var = value;
print("[subTask.lua] var after setting in evaluateData is " .. tostring(var))
return true
end
return rfsm.state {
execute = rfsm.state {
entry=function()
print('[subTask.lua] var inside execute is ' .. tostring(var))
end,
},
rfsm.trans {
src='initial',
tgt='execute',
effect=function()
print('[subTask.lua] initial -> execute')
end,
},
},
print("--- end of subTask.lua ---")
LOGS
The following logs show that the second time we enter the auxiliary SM, we use the second instance where the value of var is true (we don't want this).
[runscript.lua] Before rfsm.load("mainTask.lua")
--- start of subTask.lua ---
--- end of subTask.lua ---
--- start of subTask.lua ---
--- end of subTask.lua ---
[runscript.lua] Before rfsm.init(fsm_)
[runscript.lua] Before rfsm.run(fsm)
[mainTask.lua] initial -> Sim
[mainTask.lua] initial -> evaluate
[subTask.lua] var before setting in evaluateData is true
[subTask.lua] var after setting in evaluateData is false
[mainTask.lua] evaluate -> sub with event "internal_EvaluateDone"
[subTask.lua] initial -> execute
[subTask.lua] var inside execute is true
ATTEMPTS FOR A SOLUTION
Attempt 1
This attempt actually works in my local example (not the real system) and uses the commented line
sub_2 = sub. So the idea was to to load the state machine once, and here simply refer to that. I am wondering if the different versions of LUA have an impact on this. Locally I have Lua 5.2, on system Lua 5.1.
Edit
This attempt stops working if I simply add another dummy state inside the Seq composite state. Also there is no dependence on Lua versions.
Attempt 2
Implement a second version of load and use require instead of dofile. Then use this function in those two places inside mainTask.lua.
function load2(file)
local fileWithoutExtension = file:gsub("%.lua", "")
print(fileWithoutExtension)
local fsm = require(fileWithoutExtension)
if not is_state(fsm) then
error("rfsm.load: no valid rfsm in file '" .. tostring(file) .. "' found.")
end
return fsm
end
If I run this I get:
[runscript.lua] Before rfsm.load("mainTask.lua")
subTask
--- start of subTask.lua ---
--- end of subTask.lua ---
subTask
[runscript.lua] Before rfsm.init(fsm_)
lua: /home/anfr/rFSM-master/rfsm.lua:483: bad argument #1 to 'find' (string expected, got table)
stack traceback:
[C]: in function 'find'
/home/anfr/rFSM-master/rfsm.lua:483: in function '__resolve_path'
/home/anfr/rFSM-master/rfsm.lua:511: in function '__resolve_src'
/home/anfr/rFSM-master/rfsm.lua:551: in function 'func'
/home/anfr/rFSM-master/rfsm.lua:272: in function 'f'
/home/anfr/rFSM-master/utils.lua:246: in function 'map'
/home/anfr/rFSM-master/rfsm.lua:268: in function '__mapfsm'
/home/anfr/rFSM-master/rfsm.lua:275: in function 'f'
/home/anfr/rFSM-master/utils.lua:246: in function 'map'
/home/anfr/rFSM-master/rfsm.lua:268: in function '__mapfsm'
/home/anfr/rFSM-master/rfsm.lua:275: in function 'f'
/home/anfr/rFSM-master/utils.lua:246: in function 'map'
/home/anfr/rFSM-master/rfsm.lua:268: in function '__mapfsm'
/home/anfr/rFSM-master/rfsm.lua:283: in function 'mapfsm'
/home/anfr/rFSM-master/rfsm.lua:554: in function 'resolve_trans'
/home/anfr/rFSM-master/rfsm.lua:704: in function 'init'
runscript.lua:9: in main chunk
[C]: in ?
I don't understand why I get a table instead of a string. Unfortunately one needs to navigate in the rFSM lib code to help, but I would appreciate any hints.
Ps: Making the variable var global inside subTask.lua will give the correct behaviour. However I am trying to avoid the unnecessary overhead of loading the same script two times.
All Lua versions have the same semantics of object references assignment.
When you use
requirethe same state object is returned.The function
resolve_trans(fsm)converts.srcfields of transitions from stings to objects (tables).When you resolve the same transition object for the second time, the error is raised because
tr.srcfield is expected to be a string.