Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
HowToBound
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Karabo
HowToBound
Commits
e77c8cf4
Commit
e77c8cf4
authored
6 years ago
by
Dennis Goeries
Browse files
Options
Downloads
Patches
Plain Diff
Add FSM
parent
43db9e14
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
source/fsm.rst
+157
-0
157 additions, 0 deletions
source/fsm.rst
source/index.rst
+1
-0
1 addition, 0 deletions
source/index.rst
source/intro.rst
+44
-45
44 additions, 45 deletions
source/intro.rst
with
202 additions
and
45 deletions
source/fsm.rst
0 → 100644
+
157
−
0
View file @
e77c8cf4
The Finite State Machine (FSM)
==============================
Karabo allows to define a full finite state machine (FSM) for *cpp* devices.
The full FSM implementation will check any slot calls which lead to state
transitions if they are allowed for the current source state, and
automatically set the target state after execution of define entry, exit
and transition hooks.
Complete Example
----------------
The following is that of an okay/error outer FSM with an inner FSM defining a start/stop
behavior. It is taken from the Karabo sources and can directly be used by templating
a device to run on this FSM. The example further shows how slots are connected to events.
.. code-block:: C++
#ifndef KARABO_CORE_START_STOP_FSM_HH
#define KARABO_CORE_START_STOP_FSM_HH
#include <karabo/xms/SlotElement.hh>
#include <karabo/core/BaseFsm.hh>
#include "Device.hh"
namespace karabo {
namespace core {
class StartStopFsm : public BaseFsm {
public:
KARABO_CLASSINFO(StartStopFsm, "StartStopFsm", "1.0")
static void expectedParameters(karabo::util::Schema& expected) {
using namespace karabo::xms;
SLOT_ELEMENT(expected).key("start")
.displayedName("Start")
.description("Instructs device to go to started state")
.allowedStates(State::STOPPED)
.commit();
SLOT_ELEMENT(expected).key("stop")
.displayedName("Stop")
.description("Instructs device to go to stopped state")
.allowedStates(State::STARTED)
.commit();
SLOT_ELEMENT(expected).key("reset")
.displayedName("Reset")
.description("Resets the device in case of an error")
.allowedStates(State::ERROR)
.commit();
}
void initFsmSlots() {
SLOT(start);
SLOT(stop);
SLOT(reset);
SLOT(errorFound, std::string, std::string);
}
public:
virtual ~StartStopFsm() {}
/**************************************************************/
/* Events */
/**************************************************************/
KARABO_FSM_EVENT2(m_fsm, ErrorFoundEvent, errorFound,
std::string, std::string)
KARABO_FSM_EVENT0(m_fsm, ResetEvent, reset)
KARABO_FSM_EVENT0(m_fsm, StartEvent, start)
KARABO_FSM_EVENT0(m_fsm, StopEvent, stop)
/**************************************************************/
/* States */
/**************************************************************/
KARABO_FSM_STATE_VE_EE(Error, errorStateOnEntry, errorStateOnExit)
KARABO_FSM_STATE_VE_EE(Initialization, initializationStateOnEntry,
initializationStateOnExit)
KARABO_FSM_STATE_VE_EE(Started, startedStateOnEntry, startedStateOnExit)
KARABO_FSM_STATE_VE_EE(Stopped, stoppedStateOnEntry, stoppedStateOnExit)
/**************************************************************/
/* Transition Actions */
/**************************************************************/
KARABO_FSM_VE_ACTION2(ErrorFoundAction, errorFoundAction,
std::string, std::string);
KARABO_FSM_VE_ACTION0(ResetAction, resetAction)
KARABO_FSM_VE_ACTION0(StartAction, startAction)
KARABO_FSM_VE_ACTION0(StopAction, stopAction)
/**************************************************************/
/* AllOkState Machine */
/**************************************************************/
KARABO_FSM_TABLE_BEGIN(OkStateTransitionTable)
// Source-State, Event, Target-State, Action, Guard
Row< Stopped, StartEvent, Started, StartAction, none >,
Row< Started, StopEvent, Stopped, StopAction, none >
KARABO_FSM_TABLE_END
// Name Transition-Table Initial-State Context
KARABO_FSM_STATE_MACHINE(Ok, OkStateTransitionTable, Stopped, Self)
/**************************************************************/
/* Top Machine */
/**************************************************************/
// Source-State, Event, Target-State, Action, Guard
KARABO_FSM_TABLE_BEGIN(TransitionTable)
Row< Initialization, none, Ok, none, none >,
Row< Ok, ErrorFoundEvent, Error, ErrorFoundAction, none >,
Row< Error, ResetEvent, Ok, ResetAction, none >
KARABO_FSM_TABLE_END
// Name, Transition-Table, Initial-State, Context
KARABO_FSM_STATE_MACHINE(StateMachine, TransitionTable,
Initialization, Self)
void startFsm() {
KARABO_FSM_CREATE_MACHINE(StateMachine, m_fsm);
KARABO_FSM_SET_CONTEXT_TOP(this, m_fsm)
KARABO_FSM_SET_CONTEXT_SUB(this, m_fsm, Ok)
KARABO_FSM_START_MACHINE(m_fsm)
}
private:
KARABO_FSM_DECLARE_MACHINE(StateMachine, m_fsm);
};
}
}
#endif
\ No newline at end of file
This diff is collapsed.
Click to expand it.
source/index.rst
+
1
−
0
View file @
e77c8cf4
...
@@ -14,6 +14,7 @@ Contents:
...
@@ -14,6 +14,7 @@ Contents:
:maxdepth: 2
:maxdepth: 2
intro
intro
fsm
Indices and tables
Indices and tables
==================
==================
...
...
This diff is collapsed.
Click to expand it.
source/intro.rst
+
44
−
45
View file @
e77c8cf4
...
@@ -68,25 +68,25 @@ Consider the code of our device - ConveyorPy.py:
...
@@ -68,25 +68,25 @@ Consider the code of our device - ConveyorPy.py:
.. code-block:: python
.. code-block:: python
#!/usr/bin/env python
#!/usr/bin/env python
__author__="name.surname@xfel.eu"
__author__="name.surname@xfel.eu"
__date__ ="November, 2014, 05:26 PM"
__date__ ="November, 2014, 05:26 PM"
__copyright__="Copyright (c) 2010-2014 European XFEL GmbH Hamburg. All rights reserved."
__copyright__="Copyright (c) 2010-2014 European XFEL GmbH Hamburg. All rights reserved."
import time
import time
from karabo.bound import (
from karabo.bound import (
BOOL_ELEMENT, DOUBLE_ELEMENT, KARABO_CLASSINFO, OVERWRITE_ELEMENT, SLOT_ELEMENT,
BOOL_ELEMENT, DOUBLE_ELEMENT, KARABO_CLASSINFO, OVERWRITE_ELEMENT, SLOT_ELEMENT,
PythonDevice, State, Unit
PythonDevice, State, Unit
)
)
@KARABO_CLASSINFO("ConveyorPy", "1.3")
@KARABO_CLASSINFO("ConveyorPy", "1.3")
class ConveyorPy(PythonDevice):
class ConveyorPy(PythonDevice):
@staticmethod
@staticmethod
def expectedParameters(expected):
def expectedParameters(expected):
"""Description of device parameters statically known"""
"""Description of device parameters statically known"""
...
@@ -94,26 +94,26 @@ Consider the code of our device - ConveyorPy.py:
...
@@ -94,26 +94,26 @@ Consider the code of our device - ConveyorPy.py:
.setNewOptions(State.INIT, State.ERROR, State.STARTED, State.STOPPING, State.STOPPED, State.STARTING)
.setNewOptions(State.INIT, State.ERROR, State.STARTED, State.STOPPING, State.STOPPED, State.STARTING)
.setNewDefaultValue(State.INIT)
.setNewDefaultValue(State.INIT)
.commit(),
.commit(),
# Button definitions
# Button definitions
SLOT_ELEMENT(expected).key("start")
SLOT_ELEMENT(expected).key("start")
.displayedName("Start")
.displayedName("Start")
.description("Instructs device to go to started state")
.description("Instructs device to go to started state")
.allowedStates(State.STOPPED)
.allowedStates(State.STOPPED)
.commit(),
.commit(),
SLOT_ELEMENT(expected).key("stop")
SLOT_ELEMENT(expected).key("stop")
.displayedName("Stop")
.displayedName("Stop")
.description("Instructs device to go to stopped state")
.description("Instructs device to go to stopped state")
.allowedStates(State.STARTED)
.allowedStates(State.STARTED)
.commit(),
.commit(),
SLOT_ELEMENT(expected).key("reset")
SLOT_ELEMENT(expected).key("reset")
.displayedName("Reset")
.displayedName("Reset")
.description("Resets in case of an error")
.description("Resets in case of an error")
.allowedStates(State.ERROR)
.allowedStates(State.ERROR)
.commit(),
.commit(),
# Other elements
# Other elements
DOUBLE_ELEMENT(expected).key("targetSpeed")
DOUBLE_ELEMENT(expected).key("targetSpeed")
.displayedName("Target Conveyor Speed")
.displayedName("Target Conveyor Speed")
...
@@ -122,13 +122,13 @@ Consider the code of our device - ConveyorPy.py:
...
@@ -122,13 +122,13 @@ Consider the code of our device - ConveyorPy.py:
.assignmentOptional().defaultValue(0.8)
.assignmentOptional().defaultValue(0.8)
.reconfigurable()
.reconfigurable()
.commit(),
.commit(),
DOUBLE_ELEMENT(expected).key("currentSpeed")
DOUBLE_ELEMENT(expected).key("currentSpeed")
.displayedName("Current Conveyor Speed")
.displayedName("Current Conveyor Speed")
.description("Shows the current speed of the conveyor")
.description("Shows the current speed of the conveyor")
.readOnly()
.readOnly()
.commit(),
.commit(),
BOOL_ELEMENT(expected).key("reverseDirection")
BOOL_ELEMENT(expected).key("reverseDirection")
.displayedName("Reverse Direction")
.displayedName("Reverse Direction")
.description("Reverses the direction of the conveyor band")
.description("Reverses the direction of the conveyor band")
...
@@ -136,7 +136,7 @@ Consider the code of our device - ConveyorPy.py:
...
@@ -136,7 +136,7 @@ Consider the code of our device - ConveyorPy.py:
.allowedStates(State.STOPPED)
.allowedStates(State.STOPPED)
.reconfigurable()
.reconfigurable()
.commit(),
.commit(),
BOOL_ELEMENT(expected).key("injectError")
BOOL_ELEMENT(expected).key("injectError")
.displayedName("Inject Error")
.displayedName("Inject Error")
.description("Does not correctly stop the conveyor, such "
.description("Does not correctly stop the conveyor, such "
...
@@ -145,71 +145,71 @@ Consider the code of our device - ConveyorPy.py:
...
@@ -145,71 +145,71 @@ Consider the code of our device - ConveyorPy.py:
.reconfigurable()
.reconfigurable()
.expertAccess()
.expertAccess()
.commit(),
.commit(),
)
)
def __init__(self, configuration):
def __init__(self, configuration):
# Always call PythonDevice constructor first!
# Always call PythonDevice constructor first!
super(ConveyorPy, self).__init__(configuration)
super(ConveyorPy, self).__init__(configuration)
# Register function that will be called first
# Register function that will be called first
self.registerInitialFunction(self.initialize)
self.registerInitialFunction(self.initialize)
# Register slots
# Register slots
self.registerSlot(self.start)
self.registerSlot(self.start)
self.registerSlot(self.stop)
self.registerSlot(self.stop)
self.registerSlot(self.reset)
self.registerSlot(self.reset)
def preReconfigure(self, config):
def preReconfigure(self, config):
""" The preReconfigure hook allows to forward the configuration to some connected h/w"""
""" The preReconfigure hook allows to forward the configuration to some connected h/w"""
try:
try:
if config.has("targetSpeed"):
if config.has("targetSpeed"):
# Simulate setting to h/w
# Simulate setting to h/w
self.log.INFO("Setting to hardware: targetSpeed -> " + str(config.get("targetSpeed")))
self.log.INFO("Setting to hardware: targetSpeed -> " + str(config.get("targetSpeed")))
if config.has("reverseDirection"):
if config.has("reverseDirection"):
# Simulate setting to h/w
# Simulate setting to h/w
self.log.INFO("Setting to hardware: reverseDirection -> " + str(config.get("reverseDirection")))
self.log.INFO("Setting to hardware: reverseDirection -> " + str(config.get("reverseDirection")))
except RuntimeError as e:
except RuntimeError as e:
# You may want to indicate that the h/w failed
# You may want to indicate that the h/w failed
self.log.ERROR("'preReconfigure' method failed : {}".format(e))
self.log.ERROR("'preReconfigure' method failed : {}".format(e))
self.updateState(State.ERROR)
self.updateState(State.ERROR)
def initialize(self):
def initialize(self):
""" Initial function called after constructor but with equipped SignalSlotable under runEventLoop"""
""" Initial function called after constructor but with equipped SignalSlotable under runEventLoop"""
try:
try:
# As the Initializing state is not mentioned in the allowed states
# As the Initializing state is not mentioned in the allowed states
# nothing else is possible during this state
# nothing else is possible during this state
self.updateState(State.INIT)
self.updateState(State.INIT)
self.log.INFO("Connecting to conveyer hardware...")
self.log.INFO("Connecting to conveyer hardware...")
# Simulate some time it could need to connect and setup
# Simulate some time it could need to connect and setup
time.sleep(2.)
time.sleep(2.)
# Automatically go to the Stopped state
# Automatically go to the Stopped state
self.stop()
self.stop()
except RuntimeError as e:
except RuntimeError as e:
self.log.ERROR("'initialState' method failed : {}".format(e))
self.log.ERROR("'initialState' method failed : {}".format(e))
self.updateState(State.ERROR)
self.updateState(State.ERROR)
def start(self):
def start(self):
try:
try:
self.updateState(State.STARTING) # set this if long-lasting work follows
self.updateState(State.STARTING) # set this if long-lasting work follows
# Retrieve current values from our own device-state
# Retrieve current values from our own device-state
tgtSpeed = self.get("targetSpeed")
tgtSpeed = self.get("targetSpeed")
currentSpeed = self.get("currentSpeed")
currentSpeed = self.get("currentSpeed")
# If we do not stand still here that is an error
# If we do not stand still here that is an error
if currentSpeed > 0.0:
if currentSpeed > 0.0:
raise ValueError("Conveyer does not stand still at start-up")
raise ValueError("Conveyer does not stand still at start-up")
# Separate ramping into 50 steps
# Separate ramping into 50 steps
increase = tgtSpeed / 50.0
increase = tgtSpeed / 50.0
# Simulate a slow ramping up of the conveyor
# Simulate a slow ramping up of the conveyor
for i in range(50):
for i in range(50):
currentSpeed += increase
currentSpeed += increase
...
@@ -217,13 +217,13 @@ Consider the code of our device - ConveyorPy.py:
...
@@ -217,13 +217,13 @@ Consider the code of our device - ConveyorPy.py:
time.sleep(0.05)
time.sleep(0.05)
# Be sure to finally run with targetSpeed
# Be sure to finally run with targetSpeed
self.set("currentSpeed", tgtSpeed)
self.set("currentSpeed", tgtSpeed)
self.updateState(State.STARTED) # reached the state "Started"
self.updateState(State.STARTED) # reached the state "Started"
except RuntimeError as e:
except RuntimeError as e:
self.log.ERROR("'start' method failed : {}".format(e))
self.log.ERROR("'start' method failed : {}".format(e))
self.updateState(State.ERROR)
self.updateState(State.ERROR)
def stop(self):
def stop(self):
try:
try:
# Retrieve current value from our own device-state
# Retrieve current value from our own device-state
...
@@ -232,7 +232,7 @@ Consider the code of our device - ConveyorPy.py:
...
@@ -232,7 +232,7 @@ Consider the code of our device - ConveyorPy.py:
self.updateState(State.STOPPING) # set this if long-lasting work follows
self.updateState(State.STOPPING) # set this if long-lasting work follows
# Separate ramping into 50 steps
# Separate ramping into 50 steps
decrease = currentSpeed / 50.0
decrease = currentSpeed / 50.0
# Simulate a slow ramping down of the conveyor
# Simulate a slow ramping down of the conveyor
for i in range(50):
for i in range(50):
currentSpeed -= decrease
currentSpeed -= decrease
...
@@ -243,17 +243,17 @@ Consider the code of our device - ConveyorPy.py:
...
@@ -243,17 +243,17 @@ Consider the code of our device - ConveyorPy.py:
self.set("currentSpeed", 0.1)
self.set("currentSpeed", 0.1)
else:
else:
self.set("currentSpeed", 0.0)
self.set("currentSpeed", 0.0)
self.updateState(State.STOPPED) # reached the state "Stopped"
self.updateState(State.STOPPED) # reached the state "Stopped"
except RuntimeError as e:
except RuntimeError as e:
self.log.ERROR("'stop' method failed : {}".format(e))
self.log.ERROR("'stop' method failed : {}".format(e))
self.updateState(State.ERROR)
self.updateState(State.ERROR)
def reset(self):
def reset(self):
self.set("injectError", False)
self.set("injectError", False)
self.set("currentSpeed", 0.0)
self.set("currentSpeed", 0.0)
self.initialize()
self.initialize()
Consider the main steps of the code above, which are important to
Consider the main steps of the code above, which are important to
...
@@ -275,8 +275,7 @@ mention while writing devices in Python:
...
@@ -275,8 +275,7 @@ mention while writing devices in Python:
from karabo.bound import Worker
from karabo.bound import Worker
The current recommendation is to use NoFsm. If you need an FSM, read
The current recommendation is to use NoFsm.
:ref:`this <stateMachines>` section.
3. Place the decorator ``KARABO_CLASSINFO`` just before class definition. It has
3. Place the decorator ``KARABO_CLASSINFO`` just before class definition. It has
two parameters: "classId" and "version" similar to the corresponding C++
two parameters: "classId" and "version" similar to the corresponding C++
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment