Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions smlib/fsm.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ def __init__(self, name:str, **args) -> None:
self._awakerReason = ""
self._watchdog = None

# populate the sensityvity list for each state
# populate the sensitivity list for each state
def setSensLists(self, statesWithIos: dict) -> None:
# statesWithIos e un dizionario in cui la chiave e il nome dello stato e
# il valore un array di ingressi utilizzati dallo stato
# statesWithIos is a dictionary in which the key is the name of the state
# and the value an array of epicsIO inputs used by the state
for state, iolist in statesWithIos.items():
iodict = {}
for io in iolist:
Expand Down Expand Up @@ -91,11 +91,11 @@ def gotoState(self, state: str, *args, **kwargs) -> None:
return
self._nextstatename = state
self._nextstateargs = (args, kwargs)
# metodo eval del prossimo stato
# next state's eval method
self._nextstate = getattr(self, '%s_eval' % state)
# metodo entry del prossimo stato
# next state's entry method
self._nextentry = getattr(self, '%s_entry' % state, None)
# metodo exit del prossimo stato
# next state's exit method
self._nextexit = getattr(self, '%s_exit' % state, None)

def gotoPrevState(self, *args, **kwargs) -> None:
Expand Down Expand Up @@ -134,6 +134,8 @@ def logTimeReset(self) -> None:
def eval(self) -> bool:
'''Execute the current state'''
changed = False
if self._nextstate is None and self._curstate is None:
return False
if self._nextstate != self._curstate:
self.logD('%s => %s' % (self._curstatename, self._nextstatename))
self._prevstatename = self._curstatename
Expand Down Expand Up @@ -162,11 +164,11 @@ def eval(self) -> bool:
def eval_forever(self) -> None:
'''Main loop of the FSM'''
while not self._stop_thread:
changed = self.eval() # eval viene eseguito senza lock
self.lock() # blocca la coda degli eventi
changed = self.eval() # eval runs without lock
self.lock() # block on the queue for events
if not changed and len(self._events) == 0:
self.logD("No events to process going to sleep\n")
self._cond.wait() # la macchina va in sleep in attesa di un evento (da un IO, timer...)
self._cond.wait() # go to sleep waiting for an event (from an IO, timer ...)
self.logD('awoken')
self._process_one_event() # PROCESS ONLY IF RETURN TRUE?
self.unlock()
Expand Down
19 changes: 15 additions & 4 deletions smlib/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,25 @@
class epicsIO():
'''Class representing an IO with Epics support for a finite state machine.'''

def __init__(self, name:str) -> None:
def __init__(self, name:str, **args) -> None:
self._name = name
self._data = {} # keep all infos arriving with change callback
self._conn = False # keeps all infos arriving with connection callback

self._attached = set() # set of finite state machines using this IO
self._cond = threading.Condition()
self._pv = epics.PV(name, callback=self.chgcb, connection_callback=self.concb, auto_monitor=True)
# get the enum strings and current char_value if this is an enum PV
# this handling is required because initial char_value that the monitor
# gets is *NOT* one of the enum strings, but a numeric value as string
if args.get('is_enum', False):
# make sure we do not get callback update until the enum strings are set
self._pv = epics.PV(name, connection_callback=self.concb, auto_monitor=False)
self._pv.get_ctrlvars()
# set and fire the callback
self._pv.add_callback(self.chgcb)
self._pv.auto_monitor = True
else:
self._pv = epics.PV(name, callback=self.chgcb, connection_callback=self.concb, auto_monitor=True)

def ioname(self) -> str:
'''Return the name of the PV.'''
Expand Down Expand Up @@ -114,7 +125,6 @@ def connected(self) -> bool:
'''Return True if the PV is connected.'''
return self._conn


class fsmIOs():
'''Class representing a list of epicsIO objects.'''

Expand All @@ -126,7 +136,7 @@ def get(self, name: str, fsm: 'fsmBase', **args) -> epicsIO:

# first time this input was requested: we create and attach it
if name not in self._ios:
self._ios[name] = epicsIO(name)
self._ios[name] = epicsIO(name, **args)

# input already created: if not already attached to the fsm, trigger some
# fake events to init fsm
Expand Down Expand Up @@ -527,6 +537,7 @@ def writeAccess(self) -> bool:

def enumStrings(self) -> list:
'''Possible string values of enum PV'''
# print("XXX %s enum_strs: %s" % (self._name, self._data.get('enum_strs', None)))
return self._data.get('enum_strs', None)

def displayLimits(self) -> tuple:
Expand Down
5 changes: 3 additions & 2 deletions smlib/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,23 @@ def killAll(self, signum, frame): # pylint: disable=unused-argument
for fsm in self._fsmsList:
if fsm.is_alive():
fsm.kill()
print("Killed fsm", fsm)
print("Killed all the fsms")
if self._timerManager.is_alive(): # if no fsm is loaded it won't be alive
self._timerManager.kill()
print("Killed the timer manager", self._timerManager)
print("Killed the timer manager")

def printUnconnectedIOs(self, signum, frame): # pylint: disable=unused-argument
'''Print all the unconnected IOs.'''
ios = self._ioManager.getAll()
s = 0
print("DISCONNECTED INPUTS:")
print("DISCONNECTED INPUTS (ios %s):" % self._ioManager)
for i in ios:
if not i.connected():
print(i.ioname())
s += 1
print("Total disconnected inputs: %d out of %d!" % (s, len(ios)))
signal.pause()

def start(self, blocking=True):
'''Start all the loaded fsms.'''
Expand Down