设置和取消分层状态机的预定义超时失败(参考:https://github.com/tyarkoni/transitions/issues/198)
Setting and canceling of predefined timeouts for Hierarchical State Machine fails (ref: https://github.com/tyarkoni/transitions/issues/198)
我终于在尝试实现 https://github.com/tyarkoni/transitions/issues/198 中显示的超时机制。我的目标是在进入状态时设置默认超时(作为 TimeoutState 的构造函数参数)并在退出时取消它。退出状态时发生错误:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/IPython/core/interactiveshell.py", line 2481, in safe_execfile
self.compile if kw['shell_futures'] else None)
File "/usr/local/lib/python2.7/dist-packages/IPython/utils/py3compat.py", line 289, in execfile
builtin_mod.execfile(filename, *where)
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/hierasms.py", line 143, in <module>
collector.model.collect(a='acall1') # collecting
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/core.py", line 248, in trigger
return self.machine._process(f)
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/core.py", line 590, in _process
return trigger()
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/extensions/nesting.py", line 131, in _trigger
if t.execute(event):
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/extensions/nesting.py", line 100, in execute
return super(NestedTransition, self).execute(event_data)
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/core.py", line 174, in execute
self._change_state(event_data)
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/extensions/nesting.py", line 108, in _change_state
lvl = source_state.exit_nested(event_data, dest_state)
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/extensions/nesting.py", line 81, in exit_nested
tmp_self.exit(event_data)
TypeError: exit() takes exactly 1 argument (2 given)
这里是代码(Python 2.7.6/IPython 5.1.0/transitions 0.4.2):
from threading import Thread, Event
from transitions.extensions import HierarchicalMachine as Machine
from transitions.extensions.nesting import NestedState as State
# Set up logging
import logging
from transitions import logger
logger.setLevel(logging.DEBUG)
class Timeout(Thread):
def __init__(self, func, timeout, **kwargs):
# self.lgtout=logging.getLogger(".".join([__name__, self.__class__.__name__]))
super(Timeout, self).__init__()
self.func = func
self.kwargs = kwargs
self.cancelEvt=Event()
self.cancelEvt.clear() #make ready for delay
self.timeout = timeout
print '---->Starting countdown from: '+str(self.timeout)
self.start()
def run(self):
self.cancelEvt.wait(self.timeout)
if not self.cancelEvt.isSet():
print '---->Timeout of ' +str(self.timeout)+ ' occurred in thread'+str(self.ident)
self.func(**self.kwargs) # trigger the sm
else:
print '---->Timeout of ' +str(self.timeout)+ ' canceled in thread'+str(self.ident)
class TimeoutState(State):
def __init__(self, name, timeout=3, timeoutSmTrigger='timeOUT', *args, **kwargs):
self.timeout = timeout
self._timer=None
self.timeoutSmTrigger=timeoutSmTrigger
super(TimeoutState, self).__init__(name=name, *args, **kwargs)
print 'timeout state created: ',name
def enter(self, **kwargs):
# initialise timeout
print 'enter: '+str(kwargs)
timeout = kwargs.pop('timeout',self.timeout)
timeoutSmTrigger = kwargs.pop('timeoutSmTrigger',self.timeoutSmTrigger)
func = getattr(kwargs.get('model',None), timeoutSmTrigger)
self._timer = Timeout(func, timeout, **kwargs)
def exit(self, **kwargs):
print 'enter: '+str(kwargs)
if self._timer:
self._timer.cancelEvt.set()
class TimeoutMachine(Machine):
def __init__(self, name, states, transitions, initial, model=None):
super(TimeoutMachine,self).__init__(model=model, states=states, initial=initial, transitions=transitions,
send_event=False,
auto_transitions=False, ordered_transitions=False,
ignore_invalid_triggers=False, name=name, queued=False)
def _create_state(self, *args, **kwargs):
'''
Overwrite to create all the way states with timeout mechanism
'''
return TimeoutState(*args, **kwargs)
class CntModel(object):
def bcall(self,**kwargs):
print 'before increase: ',str(kwargs)
# logger.debug('---------bcall done')
'''
timeout indicator
'''
def timeOUT(self, **kwargs):
print 'Time out occurred'
count_states = [{'name':'1','timeout':'4','timeoutSmTrigger':'timeOUT'}, '2', '3', 'done']
# count_states = [{'name':'1'}, '2', '3', 'done']
count_trans = [
{'trigger':'increase', 'source':'1','dest':'2', 'before':'bcall'},
['increase', '2', '3'],
['decrease', '3', '2'],
['decrease', '2', '1'],
['done', '3', 'done'],
['reset', '*', '1']
]
counter = TimeoutMachine(name='Counter',states=count_states, transitions=count_trans, initial='1')
class ColModel(CntModel):
def acall(self, **kwargs):
print 'acall ',str(kwargs)
# logger.debug('-------acall done')
states = ['waiting', 'collecting', {'name': 'counting', 'children': counter}]
transitions = [
{'trigger':'collect', 'source':'*','dest':'collecting', 'after':'acall'},
['wait', '*', 'waiting'],
['count', 'collecting', 'counting_1']
]
colm = ColModel() #composed model
collector = TimeoutMachine(name='Collector', model=[colm], states=states, transitions=transitions, initial='waiting')
collector.model.collect(a='acall1') # collecting
collector.model.count() # let's see what we got
collector.model.increase(a='inc1') # counting_2
collector.model.increase(a='inc2') # counting_3
collector.model.done() # collector.state == counting_done
collector.model.wait() # collector.state == waiting
logger.handlers=[]
谢谢!
您对 TimeoutState.enter/exit
的定义缺少位置参数 (*args
)。机器将使用 self.enter(event_data)
和 event_data
作为位置参数来调用状态。
这不适合 def(self, **kwargs)
,因为 **kwargs 仅映射带有关键字的参数(例如 key=value
)。
错误发生在TimeoutState.exit
,因为之前没有进入任何状态。 Machine.initial
将或多或少地 spawn 处于传递状态的模型而无需调用 prepare
、before
或 enter
.
我建议使用继承的 class 中的方法定义(就像在提到的 github 问题中一样),除非您需要 *args
和 [=23= 附带的灵活性]:
class TimeoutState(State):
def __init__(self, name, timeout=3, timeoutSmTrigger='timeOUT', *args, **kwargs):
super(TimeoutState, self).__init__(name=name, *args, **kwargs)
def enter(self, event_data):
super(TimeoutState, self).enter(event_data)
def exit(self, event_data):
super(TimeoutState, self).exit(event_data)
您要查找的参数是 event_data
的属性。
因此,如果您将参数传递给触发器 (e.g. collector.model.count
),您将在 event_data.args
处找到位置参数,在 event_data.kwargs
处找到关键字参数。此外,您将从 event_data.model
.
获得模型
您可能想查看从何处获取参数,因为您似乎也尝试从 **kwargs
检索状态属性。这可能会导致工作代码,但似乎还有更多问题。例如,您在 getattr(kwargs.get('model', None), timeoutSmTrigger)
中链接 get 和 getattr 的方式不是很有帮助,因为如果 kwargs 不包含模型,则默认 None
肯定会引发 AttributeError
。
我终于在尝试实现 https://github.com/tyarkoni/transitions/issues/198 中显示的超时机制。我的目标是在进入状态时设置默认超时(作为 TimeoutState 的构造函数参数)并在退出时取消它。退出状态时发生错误:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/IPython/core/interactiveshell.py", line 2481, in safe_execfile
self.compile if kw['shell_futures'] else None)
File "/usr/local/lib/python2.7/dist-packages/IPython/utils/py3compat.py", line 289, in execfile
builtin_mod.execfile(filename, *where)
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/hierasms.py", line 143, in <module>
collector.model.collect(a='acall1') # collecting
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/core.py", line 248, in trigger
return self.machine._process(f)
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/core.py", line 590, in _process
return trigger()
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/extensions/nesting.py", line 131, in _trigger
if t.execute(event):
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/extensions/nesting.py", line 100, in execute
return super(NestedTransition, self).execute(event_data)
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/core.py", line 174, in execute
self._change_state(event_data)
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/extensions/nesting.py", line 108, in _change_state
lvl = source_state.exit_nested(event_data, dest_state)
File "/home/sge/workspace_neon/VS_AREA51/ocpp16j_experimental/plugged/transitions/extensions/nesting.py", line 81, in exit_nested
tmp_self.exit(event_data)
TypeError: exit() takes exactly 1 argument (2 given)
这里是代码(Python 2.7.6/IPython 5.1.0/transitions 0.4.2):
from threading import Thread, Event
from transitions.extensions import HierarchicalMachine as Machine
from transitions.extensions.nesting import NestedState as State
# Set up logging
import logging
from transitions import logger
logger.setLevel(logging.DEBUG)
class Timeout(Thread):
def __init__(self, func, timeout, **kwargs):
# self.lgtout=logging.getLogger(".".join([__name__, self.__class__.__name__]))
super(Timeout, self).__init__()
self.func = func
self.kwargs = kwargs
self.cancelEvt=Event()
self.cancelEvt.clear() #make ready for delay
self.timeout = timeout
print '---->Starting countdown from: '+str(self.timeout)
self.start()
def run(self):
self.cancelEvt.wait(self.timeout)
if not self.cancelEvt.isSet():
print '---->Timeout of ' +str(self.timeout)+ ' occurred in thread'+str(self.ident)
self.func(**self.kwargs) # trigger the sm
else:
print '---->Timeout of ' +str(self.timeout)+ ' canceled in thread'+str(self.ident)
class TimeoutState(State):
def __init__(self, name, timeout=3, timeoutSmTrigger='timeOUT', *args, **kwargs):
self.timeout = timeout
self._timer=None
self.timeoutSmTrigger=timeoutSmTrigger
super(TimeoutState, self).__init__(name=name, *args, **kwargs)
print 'timeout state created: ',name
def enter(self, **kwargs):
# initialise timeout
print 'enter: '+str(kwargs)
timeout = kwargs.pop('timeout',self.timeout)
timeoutSmTrigger = kwargs.pop('timeoutSmTrigger',self.timeoutSmTrigger)
func = getattr(kwargs.get('model',None), timeoutSmTrigger)
self._timer = Timeout(func, timeout, **kwargs)
def exit(self, **kwargs):
print 'enter: '+str(kwargs)
if self._timer:
self._timer.cancelEvt.set()
class TimeoutMachine(Machine):
def __init__(self, name, states, transitions, initial, model=None):
super(TimeoutMachine,self).__init__(model=model, states=states, initial=initial, transitions=transitions,
send_event=False,
auto_transitions=False, ordered_transitions=False,
ignore_invalid_triggers=False, name=name, queued=False)
def _create_state(self, *args, **kwargs):
'''
Overwrite to create all the way states with timeout mechanism
'''
return TimeoutState(*args, **kwargs)
class CntModel(object):
def bcall(self,**kwargs):
print 'before increase: ',str(kwargs)
# logger.debug('---------bcall done')
'''
timeout indicator
'''
def timeOUT(self, **kwargs):
print 'Time out occurred'
count_states = [{'name':'1','timeout':'4','timeoutSmTrigger':'timeOUT'}, '2', '3', 'done']
# count_states = [{'name':'1'}, '2', '3', 'done']
count_trans = [
{'trigger':'increase', 'source':'1','dest':'2', 'before':'bcall'},
['increase', '2', '3'],
['decrease', '3', '2'],
['decrease', '2', '1'],
['done', '3', 'done'],
['reset', '*', '1']
]
counter = TimeoutMachine(name='Counter',states=count_states, transitions=count_trans, initial='1')
class ColModel(CntModel):
def acall(self, **kwargs):
print 'acall ',str(kwargs)
# logger.debug('-------acall done')
states = ['waiting', 'collecting', {'name': 'counting', 'children': counter}]
transitions = [
{'trigger':'collect', 'source':'*','dest':'collecting', 'after':'acall'},
['wait', '*', 'waiting'],
['count', 'collecting', 'counting_1']
]
colm = ColModel() #composed model
collector = TimeoutMachine(name='Collector', model=[colm], states=states, transitions=transitions, initial='waiting')
collector.model.collect(a='acall1') # collecting
collector.model.count() # let's see what we got
collector.model.increase(a='inc1') # counting_2
collector.model.increase(a='inc2') # counting_3
collector.model.done() # collector.state == counting_done
collector.model.wait() # collector.state == waiting
logger.handlers=[]
谢谢!
您对 TimeoutState.enter/exit
的定义缺少位置参数 (*args
)。机器将使用 self.enter(event_data)
和 event_data
作为位置参数来调用状态。
这不适合 def(self, **kwargs)
,因为 **kwargs 仅映射带有关键字的参数(例如 key=value
)。
错误发生在TimeoutState.exit
,因为之前没有进入任何状态。 Machine.initial
将或多或少地 spawn 处于传递状态的模型而无需调用 prepare
、before
或 enter
.
我建议使用继承的 class 中的方法定义(就像在提到的 github 问题中一样),除非您需要 *args
和 [=23= 附带的灵活性]:
class TimeoutState(State):
def __init__(self, name, timeout=3, timeoutSmTrigger='timeOUT', *args, **kwargs):
super(TimeoutState, self).__init__(name=name, *args, **kwargs)
def enter(self, event_data):
super(TimeoutState, self).enter(event_data)
def exit(self, event_data):
super(TimeoutState, self).exit(event_data)
您要查找的参数是 event_data
的属性。
因此,如果您将参数传递给触发器 (e.g. collector.model.count
),您将在 event_data.args
处找到位置参数,在 event_data.kwargs
处找到关键字参数。此外,您将从 event_data.model
.
您可能想查看从何处获取参数,因为您似乎也尝试从 **kwargs
检索状态属性。这可能会导致工作代码,但似乎还有更多问题。例如,您在 getattr(kwargs.get('model', None), timeoutSmTrigger)
中链接 get 和 getattr 的方式不是很有帮助,因为如果 kwargs 不包含模型,则默认 None
肯定会引发 AttributeError
。