From 9f7875b5ecb484b51698d250f288f0dbe9d74846 Mon Sep 17 00:00:00 2001 From: Capocchi L Date: Mon, 2 Nov 2020 14:04:25 +0100 Subject: [PATCH] add FSM Lib --- Domain/FSM/FSM.py | 239 +++++++++++++++++++++++++++++++++ Domain/FSM/QFSM.amd | Bin 0 -> 5010 bytes Domain/FSM/__init__.py | 2 + Domain/FSM/fsm/SC_DC.fsm | 67 +++++++++ Domain/FSM/fsm/SC_DC_e1_e2.fsm | 86 ++++++++++++ Domain/FSM/scxml/FSM.xml | 17 +++ Domain/FSM/scxml/FSM_SC_DC.xml | 37 +++++ Domain/__init__.py | 3 +- 8 files changed, 450 insertions(+), 1 deletion(-) create mode 100644 Domain/FSM/FSM.py create mode 100644 Domain/FSM/QFSM.amd create mode 100644 Domain/FSM/__init__.py create mode 100644 Domain/FSM/fsm/SC_DC.fsm create mode 100644 Domain/FSM/fsm/SC_DC_e1_e2.fsm create mode 100644 Domain/FSM/scxml/FSM.xml create mode 100644 Domain/FSM/scxml/FSM_SC_DC.xml diff --git a/Domain/FSM/FSM.py b/Domain/FSM/FSM.py new file mode 100644 index 00000000..83cfa4a5 --- /dev/null +++ b/Domain/FSM/FSM.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python + +""" +This file contain the FSM class and the SCXMLToFSM function. +The FSM class allows to model a Finite State Machine and to simulate the transitions. +The FSM class uses the SCXMLToFSM function to translate a SCXML file into a dictionary compatible with the initFromSCXML FSM class method . +A FSM instance can be initialized in two diffrent ways: + +FSM = FSM('On', {'start':'On', 'target':'Off', 'event':'1', 'send':'1'}) + +or + +FSM = FSM() +FSM.initFromSCXML('FSM.xml') + +The file FSM.xml is exported from the QFSM software only for the free text mode (http://qfsm.sourceforge.net/). +The next state and the output are obtained using the 'next' method: + +FSM.next('1') -> ('Off', '1') +""" + +from lxml import etree + +import copy +import os, sys + +__author__ = "Laurent Capocchi" +__copyright__ = "Copyright 2016, The TIC Project" +__credits__ = ["Laurent Capocchi"] +__license__ = "GPL" +__version__ = "1.0.1" +__maintainer__ = "Laurent Capocchi" +__email__ = "capocchi@univ-corse.fr" +__status__ = "Production" + +class FSM(): + """ Finit State Machine class + """ + + def __init__(self, initState=None, transitionDict=None): + """ Constructor. + + initstate is the inital state of the FSM + transititonDcit is the state transition table that defines for all transition: + start: the starting state + target: the ending state + send: the output associated with the transition + event: the event that trigger the transition + """ + self._initstate = initState + self._transitiondict = transitionDict + + self._currentstate = self._initstate + self._currentoutput = None + self._states = [] + self._events = [] + + def setTransitionDict(self, val): + """ set the state transition table + """ + self._transitiondict = val + + def getTransitionDict(self): + """ get the state transition table + """ + return self._transitiondict + + def setInitState(self, val): + """ set the init state with the val value + """ + self._initstate = val + + def getInitState(self): + """ return the init state + """ + return self._initstate + + def getCurrentState(self): + """ return the current state + """ + return self._currentstate + + def setCurrentState(self, s): + """ set the current state + """ + self._currentstate = s + + def getCurrentOutput(self): + """ return the current output + """ + return self._currentoutput + + def setCurrentOutput(self, val): + """ set the current output + """ + self._currentoutput = val + + def getStates(self): + """ + """ + return self._states + + def setStates(self, states): + """ + """ + self._states = states + + def setEvents(self, events): + """ + """ + self._events = events + + def getEvents(self): + """ + """ + return self._events + + def addStates(self, s): + """ Add the state s into the state list + """ + if not s in self._states: + self._states.append(s) + + def addEvents(self, evt): + """ Add event evt to the event list + """ + if not evt in self._events: + self._events.append(s) + + def initFromSCXML(self, scxml): + """ Init FSM form scxml exported from QFSM (only for free text FSM) + """ + D = SCXMLtoFSM(scxml) + if D: + self.setInitState(D['initialstate']) + self.setCurrentState(self.getInitState()) + self.setTransitionDict(D['transitions']) + self.setStates(D['states']) + self.setEvents(D['events']) + + def isCurrentState(self, s): + """ return True if state s is the current state + """ + return self.getCurrentState() == s + + def isInstates(self, s): + """ return True if s is in the state list + """ + return s in self.getStates() + + def isInEvents(self, e): + """ return True if e is in the event list + """ + return e in self.getEvents() + + def next(self, event=None): + """ return the next state if event is received according to the state transition table + """ + + transitionDict = self.getTransitionDict() + currentState = self.getCurrentState() + out = [] + + if transitionDict: + self._currentoutput = None + for d in transitionDict: + if d['start'] == currentState and d['event'] == event: + self.setCurrentState(d['target']) + #self.setCurrentOutput(d['send']) + out.append(d['send']) + #break + self.setCurrentOutput(out) + return (self.getCurrentState(), self.getCurrentOutput()) + +def SCXMLtoFSM(scxml): + """ + """ + + ### test if the first arg is a file + if not os.path.isfile(scxml): + sys.stdout.write("first arg is not a file") + return None + elif not (scxml.endswith("xml") or scxml.endswith("scxml")): + sys.stdout.write("arg is not a xml file") + return None + else: + D = {'states':[], 'events':[]} + TransitionsList = [] + tree = etree.parse(scxml) + for node in tree.iter(): + if 'initialstate' in node.attrib.keys(): + D['initialstate'] = node.attrib['initialstate'] + else: + if type(node.tag) is str: + if 'state' in node.tag: + s = node.attrib['id'] + d = {'start':s} + D['states'].append(s) + elif 'transition' in node.tag: + d.update(node.attrib) + event = node.attrib['event'] + if not (event in D['events']): + D['events'].append(event) + if node.getchildren() == []: + d['send']= None + TransitionsList.append(copy.copy(d)) + elif 'send' in node.tag: + d['send']=node.attrib['event'] + TransitionsList.append(copy.copy(d)) + + D['transitions'] = TransitionsList + + return D + +if __name__ == '__main__': + + print(SCXMLtoFSM("scxml\FSM.xml")) + + FSM1 = FSM() + FSM1.initFromSCXML("scxml\FSM.xml") + print ("initial state ",FSM1.getCurrentState()) + + print ("send 1 ", FSM1.next('1')) + print ("send 0 ", FSM1.next('0')) + print ("send 0 ", FSM1.next('0')) + print ("send 1 ", FSM1.next('1')) + + FSM2 = FSM() + FSM2.initFromSCXML("scxml\FSM_SC_DC.xml") + print ("initial state ",FSM2.getCurrentState()) + print ("states ", FSM2.getStates()) + print ("events ", FSM2.getEvents()) + + print(FSM2.getTransitionDict()) + + print ("send ON ", FSM2.next('ON')) + print ("send OFF ", FSM2.next('OFF')) + print ("send DC ", FSM2.next('DC')) + print ("send ON ", FSM2.next('ON')) diff --git a/Domain/FSM/QFSM.amd b/Domain/FSM/QFSM.amd new file mode 100644 index 0000000000000000000000000000000000000000..487995fec725452f9ac4013e0f3db7d195d98d00 GIT binary patch literal 5010 zcmb_g&2Hn!5q5Sr$-03A2(q_adOcW34rD@-<)1OuL1yi7BFy+_N6sL~W+Al57S&^l zWY|rOmD^tO40(80*68-h?b8#dT5mf;f=voD*g|iS)C-+r zp;*`SdZyDgv5}sdblm`mep0$wR@gd+&jWd;@+=HzM#$?TR;l@`qTqty50@|G+0~1Y zJnZccggl3(H5(Gw>jm9@(C_xULOwU4PHnUMyPM~7u^)6lI4%k6;?bi=a^!;>O5+ju zL+~#cv*+1dnRG}vnW|6+{`o6CQwx(7wVut@hdHk&uXN~Yj!SK=inZ{xR2Y@XxD}$h z$4`9=d`8*4e#)%(m+KCAQ`X8K;w$kAFNTV=z3I>HiP&znTJrqq%aJ@|WmEXh<;*p~ z+d^yuSaLizsd3|R+v;T6;p$J(HBNW98wBVGI~}T!)256`Jj*}JQtOH`bXgHV+V=Ku zec4k7AsHuJO59KAQD(kBpnI(K7n@DpYrsEP699FdH%zkduI78A__=j7#osp5H+s!` z`pm9(0N2gH1slQGC7%eTFaqr=%P6~%rxYsU^JMV!R9>Mc)uLb} zwMdSMzBkr^6l<9ca^+@$v9!affDR5cTR=RR8BGY0#S&e*YEj`#r7`*g+7c0#Ru|}8 zNI80nnU@JS0w~%BK7&FhnTpy#4Ml;aGn$b~m4=3Cxj&80{&@M^TS^@0bVR@A7uW&< zAIquN147dUrU$b(o;0lJdI(8F9cy=176n@1H8ba&ell_(~^ak zmW>7aIvC7=56cN`o*35#otNG~0CWuzOWS6yF5k2Jvn+{*$UDR_?~0Y`y@_I)If`_K zVY(_WAIPc_a&&UD030FBL(SWg7wA^)9WUt%D>o#4zVihI80Iv3*{XL4^Rin0bcWoL zEIO4e=~VJLocECSalc5z2jE=gr0$kAg10HoERLF5PQ^B~wI4M;j5WSDRD}pxodlh$ z0tihO)1hP{SvoD}x}fT0hp<{v`M!~7ch%0dF9h>#M^=-y{S683cDh(%W@qO53ZU41 zAVYMf<2nEjou}3&%PPrlX(Q2%w%WjVs`ODs8sFO}-Zzw1VTMt4rO}l5vOW|0j#X6! z#c-`PazXfGL=et09L;=C>F|w4SXJt-4^QpQx_}3J>k4tK^epXRUe8Q8qgRv!uPZaOfmfw<^?0RLOi--wx0<#q zOQ@*DP4j9fmjC(vfB*jP@SmS-ZPE7^cr~PnW9BzYJ{<;;av_~H#)XarE@(gXDNE!% z(GD-oJt{I7sRi|Sabv=}L`P>?lHj}w>qXud;dneLP2x;C9t)~gk?)Ikk`Kho^OHZ9 z-R?nnUS#4_{6MG=D{@T%4qW-6Q0*u`f)>&# z@?)V!Exe}SfZr1NW9%k*uPa8aM=FYr^&u8}J)wsD?RJG?g1(4pmGAe(u-6C0(Eyjn z`*gwM%O{0V37D}84Xs*)Q0_(EI}k9UcL+7P5BE5mydqUc6xH6bh!KYvw#KkDhK(^S zi(wZo5FYoSW<#hQwJHgZVH&cE!?eSji!Q_&M(kH~@I@lKo0$X)a*Xw!xPn>zKAF|u zhgoKhx05j)B4Jg3fR&bjj){uK^>Lhc$J0c`L~|p5nEa~6tEr7iJItSjOk5_ zM*@yukzX4c@$4>E`DK>K9wVeg=G*Z&GJq{X5qtZT8*LrkGR+i@(gUSrpO{QUkH`v} z18Wm{9$>pq+v#LXGc9>Fv~pM}MxjHaW=Ti2XKdc4F~=|_D=Tx@Gj)? zK{!rY^w@)bR+cYu=t*ne2Qd4P$naX_#`IbgtkAzpTqB`zoe)C14BKzQUCw| literal 0 HcmV?d00001 diff --git a/Domain/FSM/__init__.py b/Domain/FSM/__init__.py new file mode 100644 index 00000000..a4a46e78 --- /dev/null +++ b/Domain/FSM/__init__.py @@ -0,0 +1,2 @@ +__all__=[ +] \ No newline at end of file diff --git a/Domain/FSM/fsm/SC_DC.fsm b/Domain/FSM/fsm/SC_DC.fsm new file mode 100644 index 00000000..19a877b6 --- /dev/null +++ b/Domain/FSM/fsm/SC_DC.fsm @@ -0,0 +1,67 @@ + + + + + + + + + On + Off + DC + + 0 + 1 + OFF + OFF + + + 0 + 0 + ON + + + + 0 + 2 + DC + DC + + + 1 + 0 + ON + ON + + + 1 + 1 + OFF + + + + 1 + 2 + DC + DC + + + 2 + 2 + DC + + + + 2 + 0 + ON + ON + + + 2 + 1 + OFF + OFF + + + diff --git a/Domain/FSM/fsm/SC_DC_e1_e2.fsm b/Domain/FSM/fsm/SC_DC_e1_e2.fsm new file mode 100644 index 00000000..2d81851e --- /dev/null +++ b/Domain/FSM/fsm/SC_DC_e1_e2.fsm @@ -0,0 +1,86 @@ + + + + + + + + + ON + OFF + DC_ON + DC_OFF + + 0 + 1 + e1 + off + + + 0 + 1 + e2 + off + + + 0 + 3 + e1e2 + dc_off + + + 1 + 2 + e1e2 + dc_on + + + 1 + 0 + e2 + on + + + 1 + 0 + e1 + on + + + 2 + 3 + e1e2 + dc_off + + + 2 + 1 + e2 + off + + + 2 + 1 + e1 + off + + + 3 + 2 + e1e2 + dc_on + + + 3 + 0 + e1 + on + + + 3 + 0 + e2 + on + + + diff --git a/Domain/FSM/scxml/FSM.xml b/Domain/FSM/scxml/FSM.xml new file mode 100644 index 00000000..db4fe30e --- /dev/null +++ b/Domain/FSM/scxml/FSM.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Domain/FSM/scxml/FSM_SC_DC.xml b/Domain/FSM/scxml/FSM_SC_DC.xml new file mode 100644 index 00000000..efedb26f --- /dev/null +++ b/Domain/FSM/scxml/FSM_SC_DC.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Domain/__init__.py b/Domain/__init__.py index 035a3f6d..601f2fdd 100644 --- a/Domain/__init__.py +++ b/Domain/__init__.py @@ -1,5 +1,6 @@ __all__ = [ "Basic", "Generator", - "Collector" + "Collector", + 'FSM' ]