0

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.

AngelosFr
  • 115
  • 1
  • 9

1 Answers1

1

I am wondering if the different versions of LUA have an impact on this

All Lua versions have the same semantics of object references assignment.


as you can see, inside mainTask.lua, we are loading the subTask.lua twice via rfsm.load("subTask.lua")

When you use require the same state object is returned.


I don't understand why I get a table instead of a string

The function resolve_trans(fsm) converts .src fields of transitions from stings to objects (tables).
When you resolve the same transition object for the second time, the error is raised because tr.src field is expected to be a string.

Egor Skriptunoff
  • 23,359
  • 2
  • 34
  • 64
  • In this line ```return utils.andt(mapfsm(__resolve_trans, fsm, is_trans))``` we are calling ```__resolve_trans``` which is ```local function __resolve_trans(tr, parent) return __resolve_src(tr, parent) and __resolve_tgt(tr, parent) end ``` Where are we getting the ```tr``` and ```parent``` from? – AngelosFr Jun 23 '22 at 12:19
  • parent = states, tr = element of states which is a transition – Egor Skriptunoff Jun 23 '22 at 13:37
  • any recommendations and/or pointers on how I should modify the code? – AngelosFr Jun 28 '22 at 06:42
  • `This results in having two instances of the auxiliary SM, but I want to have one instead.` - Why optimizing it? Leave it as it is. :-) – Egor Skriptunoff Jun 28 '22 at 08:37