Skip to content

Feature Request: While-in-state callbacks #29

@MichaelBonnet

Description

@MichaelBonnet

Hello! This is a great project I'm a huge fan of.

My use case is vehicle control. Having a state machine for this is key. While not a hard requirement, it would be useful to decouple "state entry" logic (the on* callbacks) from my "do certain things while in state continuously on loop" callbacks. Something like:

local machine = require('statemachine')

local fsm = machine.create({
  initial = 'startup',
  events = {
    { name = 'startup2safe',         from = 'startup',    to = 'safe'       },
    { name = 'normalOps2safe',       from = 'normalOps',  to = 'safe'       },
    { name = 'diagnostic2safe',      from = 'diagnostic', to = 'safe'       },
    { name = 'startup2diagnostic',   from = 'startup',    to = 'diagnostic' }
    { name = 'diagnostic2normalOps', from = 'diagnostic', to = 'normalOps'  }
    { name = 'safe2normalOps',       from = 'safe',       to = 'normalOps'  }
    { name = 'startup2normalOps',    from = 'startup',    to = 'normalOps'  }
  },
  callbacks = {
    -- on-state-entry callbacks
    onstartup2safe         = function(self, event, from, to) print('transitioned from startup to safe')         end,
    onnormalOps2safe       = function(self, event, from, to) print('transitioned from normalops to safe')       end,
    ondiagnostic2safe      = function(self, event, from, to) print('transitioned from diagnostic to safe')      end,
    onstartup2diagnostic   = function(self, event, from, to) print('transitioned from startup to diagnostic')   end,
    ondiagnostic2normalOps = function(self, event, from, to) print('transitioned from diagnostic to normalOps') end,
    onsafe2normalOps       = function(self, event, from, to) print('transitioned from safe to normalOps')       end,
    onstartup2normalOps    = function(self, event, from, to) print('transitioned from startup to normalOps')    end,

    -- DESIRE: while-in-state callbacks
    whileinstartup    = function(self, event, from, to) print('still in startup state')    end,
    whileinsafe       = function(self, event, from, to) print('still in safe state')       end,
    whileindiagnostic = function(self, event, from, to) print('still in diagnostic state') end,
    whileinnormalops  = function(self, event, from, to) print('still in normalops state')  end,
  }
})

-- On startup, we'd just see 'still in startup state' print over and over until we transition elsewhere.

This is essentially roughly equivalent to something like:

local machine = require('statemachine')

-- ============================ --
-- === State Loop Functions === --
-- ============================ --

local function whileInSafe()
  while true do
    print('still in safe state')
  end
end

local function whileInStartup()
  while true do
    print('still in startup state')
  end
end

local function whileInDiagnostic()
  while true do
    print('still in diagnostic state')
  end
end

local function whileInNormalOps()
  while true do
    print('still in normalOps state')
  end
end

-- ================================== --
-- === Event Transition Functions === --
-- ================================== --

local function startup2safe()
  print('transitioned from startup to safe')
  whileInSafe()
end

local function normalOps2safe()
  print('transitioned from normalOps to safe')
  whileInSafe()
end

local function diagnostic2safe()
  print('transitioned from diagnostic to safe')
  whileInSafe()
end

local function startup2diagnostic()
  print('transitioned from startup to diagnostic')
  whileInDiagnostic()
end

local function diagnostic2normalOps()
  print('transitioned from diagnostic to normalOps')
  whileInNormalops()
end

local function safe2normalOps()
  print('transitioned from safe to normalOps')
  whileInNormalops()
end

local function startup2normalOps()
  print('transitioned from startup to normalOps')
  whileInNormalops()
end

-- =========== --
-- === FSM === --
-- =========== --

local fsm = machine.create({
  initial = 'startup',
  events = {
    { name = 'startup2safe',         from = 'startup',    to = 'safe'       },
    { name = 'normalOps2safe',       from = 'normalOps',  to = 'safe'       },
    { name = 'diagnostic2safe',      from = 'diagnostic', to = 'safe'       },
    { name = 'startup2diagnostic',   from = 'startup',    to = 'diagnostic' }
    { name = 'diagnostic2normalOps', from = 'diagnostic', to = 'normalOps'  }
    { name = 'safe2normalOps',       from = 'safe',       to = 'normalOps'  }
    { name = 'startup2normalOps',    from = 'startup',    to = 'normalOps'  }
  },
  callbacks = {
    onstartup2safe         = function(self, event, from, to) startup2safe()         end,
    onnormalOps2safe       = function(self, event, from, to) normalOps2safe()       end,
    ondiagnostic2safe      = function(self, event, from, to) diagnostic2safe()      end,
    onstartup2diagnostic   = function(self, event, from, to) startup2diagnostic()   end,
    ondiagnostic2normalOps = function(self, event, from, to) diagnostic2normalOps() end,
    onsafe2normalOps       = function(self, event, from, to) safe2normalOps()       end,
    onstartup2normalOps    = function(self, event, from, to) startup2normalOps()    end,
  }
})

-- On startup, we'd just see 'still in startup state' print over and over until we transition elsewhere.

Not exactly sure how this would work to detect "transition out, abort the while-in-state callback". Maybe a global that each callback refers to as a check condition for the while loop continuing?

Happy to learn more about how to do this as-is in the framework.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions