Passed
Pull Request — master (#348)
by
unknown
02:40
created

asyncua.common.statemachine.main()   B

Complexity

Conditions 3

Size

Total Lines 54
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 46
nop 0
dl 0
loc 54
rs 8.7672
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
'''
2
https://reference.opcfoundation.org/v104/Core/docs/Part10/
3
https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.1/
4
Basic statemachines described in OPC UA Spec.:
5
StateMachineType
6
FiniteStateMachineType
7
ExclusiveLimitStateMachineType
8
FileTransferStateMachineType
9
ProgramStateMachineType
10
ShelvedStateMachineType
11
Relevant information:
12
Overview - https://reference.opcfoundation.org/v104/Core/docs/Part10/5.2.3/#5.2.3.1
13
States - https://reference.opcfoundation.org/v104/Core/docs/Part10/5.2.3/#5.2.3.2
14
Transitions - https://reference.opcfoundation.org/v104/Core/docs/Part10/5.2.3/#5.2.3.3
15
Events - https://reference.opcfoundation.org/v104/Core/docs/Part10/5.2.5/
16
'''
17
import asyncio, logging, datetime
18
19
#FIXME 
20
# -change to relativ imports!
21
# -remove unused imports
22
from asyncua import Server, ua, Node
23
from asyncua.common.event_objects import TransitionEvent, ProgramTransitionEvent
24
25
_logger = logging.getLogger(__name__)
26
27
class StateMachineTypeClass(object):
28
    '''
29
    Implementation of an StateMachineType (most basic type)
30
    CurrentState: Mandatory "StateVariableType"
31
    LastTransition: Optional "TransitionVariableType"
32
    Generates TransitionEvent's
33
    '''
34
    def __init__(self, server=None, parent=None, idx=None, name=None):
35
        if not isinstance(server, Server): 
36
            raise ValueError
37
        if not isinstance(parent, Node): 
38
            raise ValueError
39
        if idx == None:
40
            idx = parent.nodeid.NamespaceIndex
41
        if name == None:
42
            name = "StateMachine"
43
        self.locale = "en-US"
44
        self._server = server
45
        self._parent = parent
46
        self._state_machine_node = None
47
        self._state_machine_type = ua.NodeId(2299, 0) #StateMachineType
48
        self._name = name
49
        self._idx = idx
50
        self._optionals = False
51
        self._current_state_node = None
52
        self._current_state_id_node = None
53
        self._current_state_name_node = None
54
        self._current_state_number_node = None
55
        self._last_transition_node = None
56
        self._last_transition_id_node = None
57
        self._last_transition_name_node = None
58
        self._last_transition_number_node = None
59
        self._last_transition_transitiontime_node = None
60
        self._last_transition_effectivetransitiontime_node = None
61
        self._evgen = None
62
        self.evtype = TransitionEvent()
63
64
    class State(object):
65
        '''
66
        Helperclass for States (StateVariableType)
67
        https://reference.opcfoundation.org/v104/Core/docs/Part5/B.4.3/
68
        name: type string will be converted automatically to qualifiedname 
69
            -> Name is a QualifiedName which uniquely identifies the current state within the StateMachineType.
70
        id: Id is a name which uniquely identifies the current state within the StateMachineType. A subtype may restrict the DataType.
71
        number: Number is an integer which uniquely identifies the current state within the StateMachineType.
72
        '''
73
        def __init__(self, name, id=0, node=None, issub=False):
74
            self.name = name
75
            self.id = str(id)
76
            self.number = id
77
            self.node = node #will be written from statemachine.add_state() or you need to overwrite it if the state is part of xml
78
            self.issub = issub #true if it is a substate
79
80
    class Transition(object):
81
        '''
82
        Helperclass for Transitions (TransitionVariableType)
83
        https://reference.opcfoundation.org/v104/Core/docs/Part5/B.4.4/
84
        name: type string will be converted automatically to qualifiedname 
85
            -> Name is a QualifiedName which uniquely identifies a transition within the StateMachineType.
86
        id: Id is a name which uniquely identifies a Transition within the StateMachineType. A subtype may restrict the DataType.
87
        number: Number is an integer which uniquely identifies the current state within the StateMachineType.
88
        transitiontime: TransitionTime specifies when the transition occurred.
89
        effectivetransitiontime: EffectiveTransitionTime specifies the time when the current state or one of its substates was entered. 
90
        If, for example, a StateA is active and – while active – switches several times between its substates SubA and SubB, 
91
        then the TransitionTime stays at the point in time where StateA became active whereas the EffectiveTransitionTime changes 
92
        with each change of a substate.
93
        '''
94
        def __init__(self, name, id=0, node=None, issub=False):
95
            self.name = name
96
            self.id = str(id)
97
            self.number = id
98
            self._transitiontime = datetime.datetime.utcnow() #will be overwritten from _write_transition()
99
            self._effectivetransitiontime = datetime.datetime.utcnow() #will be overwritten from _write_transition()
100
            self.node = node #will be written from statemachine.add_state() or you need to overwrite it if the state is part of xml
101
            self.issub = issub #true if it is a transition between substates
102
103
    async def install(self, optionals=False):
104
        '''
105
        setup adressspace
106
        '''
107
        self._optionals = optionals
108
        self._state_machine_node = await self._parent.add_object(
109
            self._idx, 
110
            self._name, 
111
            objecttype=self._state_machine_type, 
112
            instantiate_optional=optionals
113
            )
114
        if self._optionals:
115
            #FIXME somehow propertys dont get added if i instantiate with oprionals
116
            self._last_transition_node = await self._state_machine_node.get_child(["LastTransition"])
117
            self._last_transition_transitiontime_node = await self._last_transition_node.add_property(0, "TransitionTime", ua.Variant(datetime.datetime.utcnow(), varianttype=ua.VariantType.DateTime))
118
        await self.init(self._state_machine_node)
119
    
120
    async def init(self, statemachine):
121
        '''
122
        initialize and get subnodes
123
        '''
124
        self._current_state_node = await statemachine.get_child(["CurrentState"])
125
        current_state_props = await self._current_state_node.get_properties()
126
        for prop in current_state_props:
127
            dn = await prop.read_display_name()
128
            if dn.Text == "Id":
129
                self._current_state_id_node = await self._current_state_node.get_child(["Id"])
130
            elif dn.Text == "Name":
131
                self._current_state_name_node = await self._current_state_node.get_child(["Name"])
132
            elif dn.Text == "Number":
133
                self._current_state_number_node = await self._current_state_node.get_child(["Number"])
134
            else:
135
                _logger.warning(f"{await statemachine.read_browse_name()} CurrentState Unknown propertie: {dn.Text}")
136
        if self._optionals:
137
            self._last_transition_node = await statemachine.get_child(["LastTransition"])
138
            last_transition_props = await self._last_transition_node.get_properties()
139
            for prop in last_transition_props:
140
                dn = await prop.read_display_name()
141
                if dn.Text == "Id":
142
                    self._last_transition_id_node = await self._last_transition_node.get_child(["Id"])
143
                elif dn.Text == "Name":
144
                    self._last_transition_name_node = await self._last_transition_node.get_child(["Name"])
145
                elif dn.Text == "Number":
146
                    self._last_transition_number_node = await self._last_transition_node.get_child(["Number"])
147
                elif dn.Text == "TransitionTime" and not self._last_transition_transitiontime_node:
148
                    self._last_transition_transitiontime_node = await self._last_transition_node.get_child(["TransitionTime"])
149
                elif dn.Text == "EffectiveTransitionTime":
150
                    self._last_transition_effectivetransitiontime_node = await self._last_transition_node.get_child(["EffectiveTransitionTime"])
151
                else:
152
                    _logger.warning(f"{await statemachine.read_browse_name()} LastTransition Unknown propertie: {dn.Text}")
153
        self._evgen = await self._server.get_event_generator(self.evtype, self._state_machine_node)
154
155
    async def change_state(self, state, transition=None, event_msg=None, severity=500):
156
        '''
157
        method to change the state of the statemachine
158
        state: "self.State" mandatory
159
        transition: "self.Transition" optional
160
        event_msg: "string/LocalizedText" optional
161
        severity: "Int" optional
162
        '''
163
        #FIXME check StateType exist
164
        #FIXME check TransitionTypeType exist
165
        await self._write_state(state)
166
        if transition:
167
            await self._write_transition(transition)
168
        if event_msg:
169
            if isinstance(event_msg, (type(""))):
170
                event_msg = ua.LocalizedText(event_msg, self.locale)
171
            self._evgen.event.Message = event_msg
172
            self._evgen.event.Severity = severity
173
            await self._evgen.trigger()
174
175
    async def _write_state(self, state):
176
        if not isinstance(state, self.State):
177
            raise ValueError
178
        await self._current_state_node.write_value(ua.LocalizedText(state.name, self.locale), ua.VariantType.LocalizedText)
179
        if state.node:
180
            if self._current_state_id_node:
181
                await self._current_state_id_node.write_value(state.node.nodeid, ua.VariantType.NodeId)
182
            if self._current_state_name_node:
183
                await self._current_state_name_node.write_value(state.name, ua.VariantType.QualifiedName)
184
            if self._current_state_number_node:
185
                await self._current_state_number_node.write_value(state.number, ua.VariantType.UInt32)
186
187
    async def _write_transition(self, transition):
188
        '''
189
        transition: self.Transition
190
        issub: boolean (true if it is a transition between substates)
191
        '''
192
        if not isinstance(transition, self.Transition):
193
            raise ValueError
194
        if transition.issub == False:
195
            transition._transitiontime = datetime.datetime.utcnow()
196
        transition._effectivetransitiontime = datetime.datetime.utcnow()
197
        await self._last_transition_node.write_value(ua.LocalizedText(transition.name, self.locale), ua.VariantType.LocalizedText)
198
        if transition.node:
199
            if self._last_transition_id_node:
200
                await self._last_transition_id_node.write_value(transition.node.nodeid, ua.VariantType.NodeId)
201
            if self._last_transition_name_node:
202
                await self._last_transition_name_node.write_value(ua.QualifiedName(transition.name, self._idx), ua.VariantType.QualifiedName)
203
            if self._last_transition_number_node:
204
                await self._last_transition_number_node.write_value(transition.number, ua.VariantType.UInt32)
205
            if self._last_transition_transitiontime_node:
206
                await self._last_transition_transitiontime_node.write_value(transition._transitiontime, ua.VariantType.DateTime)
207
            if self._last_transition_effectivetransitiontime_node:
208
                await self._last_transition_effectivetransitiontime_node.write_value(transition._effectivetransitiontime, ua.VariantType.DateTime)
209
            
210
    async def add_state(self, state, state_type=ua.NodeId(2307, 0), optionals=False):
211
        '''
212
        this method adds a state object to the statemachines address space
213
        state: self.State,
214
        InitialStateType: ua.NodeId(2309, 0),
215
        StateType: ua.NodeId(2307, 0),
216
        ChoiceStateType: ua.NodeId(15109,0),
217
        '''
218
        if not isinstance(state, self.State):
219
            raise ValueError
220
        state.node = await self._state_machine_node.add_object(
221
            self._idx, 
222
            state.name, 
223
            objecttype=state_type, 
224
            instantiate_optional=optionals
225
            )
226
        state_number = await state.node.get_child(["StateNumber"])
227
        await state_number.write_value(state.number, ua.VariantType.UInt32)
228
        return state.node
229
230
    async def add_transition(self, transition, transition_type=ua.NodeId(2310, 0), optionals=False):
231
        '''
232
        this method adds a transition object to the statemachines address space
233
        transition: self.Transition,
234
        transition_type: ua.NodeId(2310, 0),
235
        '''
236
        if not isinstance(transition, self.Transition):
237
            raise ValueError
238
        transition.node = await self._state_machine_node.add_object(
239
            self._idx, 
240
            transition.name, 
241
            objecttype=transition_type, 
242
            instantiate_optional=optionals
243
            )
244
        transition_number = await transition.node.get_child(["TransitionNumber"])
245
        await transition_number.write_value(transition.number, ua.VariantType.UInt32)
246
        return transition.node
247
248
    async def remove(self, nodes):
249
        #FIXME
250
        raise NotImplementedError
251
252
class FiniteStateMachineTypeClass(StateMachineTypeClass):
253
    '''
254
    Implementation of an FiniteStateMachineType a little more advanced than the basic one
255
    if you need to know the avalible states and transition from clientside
256
    '''
257
    def __init__(self, server=None, parent=None, idx=None, name=None):
258
        super().__init__(server, parent, idx, name)
259
        if name == None:
260
            name = "FiniteStateMachine"
261
        self._state_machine_type = ua.NodeId(2771, 0)
262
        self._avalible_states_node = None
263
        self._avalible_transitions_node = None
264
265
    async def install(self, optionals=False):
266
        '''
267
        setup adressspace and initialize 
268
        '''
269
        self._optionals = optionals
270
        self._state_machine_node = await self._parent.add_object(
271
            self._idx, 
272
            self._name, 
273
            objecttype=self._state_machine_type, 
274
            instantiate_optional=optionals
275
            )
276
277
    async def init(self, avalible_states, avalible_transitions, ):
278
        #FIXME get children and map children
279
        #await self.find_all_states()
280
        await self.set_avalible_states(avalible_states)
281
        #await self.find_all_transitions()
282
        await self.set_avalible_transitions(avalible_transitions)
283
284
    async def set_avalible_states(self, states):
285
        #check if its list
286
        await self._avalible_states_node.write_value(states, varianttype=ua.VariantType.NodeId)
287
288
    async def set_avalible_transitions(self, transitions):
289
        #check if its list
290
        await self._avalible_transitions_node.write_value(transitions, varianttype=ua.VariantType.NodeId)
291
292
    async def find_all_states(self):
293
        return NotImplementedError
294
    
295
    async def find_all_transitions(self):
296
        return NotImplementedError
297
298
class ExclusiveLimitStateMachineTypeClass(FiniteStateMachineTypeClass):
299
    '''
300
    NOT IMPLEMENTED "ExclusiveLimitStateMachineType"
301
    '''
302
    def __init__(self, server=None, parent=None, idx=None, name=None):
303
        super().__init__(server, parent, idx, name)
304
        if name == None:
305
            name = "ExclusiveLimitStateMachine"
306
        self._state_machine_type = ua.NodeId(9318, 0)
307
        raise NotImplementedError
308
309
class FileTransferStateMachineTypeClass(FiniteStateMachineTypeClass):
310
    '''
311
    NOT IMPLEMENTED "FileTransferStateMachineType"
312
    '''
313
    def __init__(self, server=None, parent=None, idx=None, name=None):
314
        super().__init__(server, parent, idx, name)
315
        if name == None:
316
            name = "FileTransferStateMachine"
317
        self._state_machine_type = ua.NodeId(15803, 0)
318
        raise NotImplementedError
319
320
class ProgramStateMachineTypeClass(FiniteStateMachineTypeClass):
321
    '''
322
    https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.3/
323
    Implementation of an ProgramStateMachine its quite a complex statemachine with the 
324
    optional possibility to make the statchange from clientside via opcua-methods
325
    '''
326
    def __init__(self, server=None, parent=None, idx=None, name=None):
327
        super().__init__(server, parent, idx, name)
328
        if name == None:
329
            name = "ProgramStateMachine"
330
        self._state_machine_type = ua.NodeId(2391, 0)
331
        self.evtype = ProgramTransitionEvent()
332
333
        # 5.2.3.2 ProgramStateMachineType states
334
        self._ready_state_node = None #State node
335
        self._halted_state_node = None #State node
336
        self._running_state_node = None #State node
337
        self._suspended_state_node = None #State node
338
339
        # 5.2.3.3 ProgramStateMachineType transitions
340
        self._halted_to_ready_node = None #Transition node
341
        self._ready_to_running_node = None #Transition node
342
        self._running_to_halted_node = None #Transition node
343
        self._running_to_ready_node = None #Transition node
344
        self._running_to_suspended_node = None #Transition node
345
        self._suspended_to_running_node = None #Transition node
346
        self._suspended_to_halted_node = None #Transition node
347
        self._suspended_to_ready_node = None #Transition node
348
        self._ready_to_halted_node = None #Transition node
349
350
        # 5.2.3.2 ProgramStateMachineType states
351
        self._halted_state_id_node = None #State property (StateNumber value 11)
352
        self._ready_state_id_node = None #State property (StateNumber value 12)
353
        self._running_state_id_node = None #State property (StateNumber value 13)
354
        self._suspended_state_id_node = None #State property (StateNumber value 14)
355
356
        # 5.2.3.3 ProgramStateMachineType transitions
357
        self._halted_to_ready_id_node = None #Transition property (TransitionNumber value 1)
358
        self._ready_to_running_id_node = None #Transition property (TransitionNumber value 2)
359
        self._running_to_halted_id_node = None #Transition property (TransitionNumber value 3)
360
        self._running_to_ready_id_node = None #Transition property (TransitionNumber value 4)
361
        self._running_to_suspended_id_node = None #Transition property (TransitionNumber value 5)
362
        self._suspended_to_running_id_node = None #Transition property (TransitionNumber value 6)
363
        self._suspended_to_halted_id_node = None #Transition property (TransitionNumber value 7)
364
        self._suspended_to_ready_id_node = None #Transition property (TransitionNumber value 8)
365
        self._ready_to_halted_id_node = None #Transition property (TransitionNumber value 9)
366
367
        # 4.2.7 Program Control Methods (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.7/)
368
        self._halt_method_node = None #uamethod node
369
        self._reset_method_node = None #uamethod node
370
        self._resume_method_node = None #uamethod node
371
        self._start_method_node = None #uamethod node
372
        self._suspend_method_node = None #uamethod node
373
374
        #can be overwritten if you want a different language
375
        self.localizedtext_ready = ua.LocalizedText("Ready", "en-US")
376
        self.localizedtext_running = ua.LocalizedText("Running", "en-US")
377
        self.localizedtext_halted = ua.LocalizedText("Halted", "en-US")
378
        self.localizedtext_suspended= ua.LocalizedText("Suspended", "en-US")
379
        self.localizedtext_halted_to_ready = ua.LocalizedText("HaltedToReady", "en-US")
380
        self.localizedtext_ready_to_running = ua.LocalizedText("ReadyToRunning", "en-US")
381
        self.localizedtext_running_to_halted = ua.LocalizedText("RunningToHalted", "en-US")
382
        self.localizedtext_running_to_ready = ua.LocalizedText("RunningToReady", "en-US")
383
        self.localizedtext_running_to_suspended = ua.LocalizedText("RunningToSuspended", "en-US")
384
        self.localizedtext_suspended_to_running = ua.LocalizedText("SuspendedToRunning", "en-US")
385
        self.localizedtext_suspended_to_halted = ua.LocalizedText("SuspendedToHalted", "en-US")
386
        self.localizedtext_suspended_to_ready = ua.LocalizedText("SuspendedToReady", "en-US")
387
        self.localizedtext_ready_to_halted = ua.LocalizedText("ReadyToHalted", "en-US")
388
389
    async def install(self, optionals=False):
390
        '''
391
        setup adressspace and initialize 
392
        '''
393
        self._optionals = optionals
394
        self._state_machine_node = await self._parent.add_object(
395
            self._idx, 
396
            self._name, 
397
            objecttype=self._state_machine_type, 
398
            instantiate_optional=optionals
399
            )
400
        #FIXME get children and map children
401
402
    #Transition
403
    async def HaltedToReady(self):
404
        await self._current_state.write_value(
405
            self.localizedtext_ready,
406
            varianttype=ua.VariantType.LocalizedText
407
            ) 
408
        await self._current_state_id.write_value(
409
            self._ready_state.nodeid, 
410
            varianttype=ua.VariantType.NodeId
411
            )
412
        await self._last_transition.write_value(
413
            self.localizedtext_halted_to_ready,
414
            varianttype=ua.VariantType.LocalizedText
415
            ) 
416
        await self._last_transition_id.write_value(
417
            self._halted_to_ready.nodeid,
418
            varianttype=ua.VariantType.NodeId
419
            )
420
        #FIXME 
421
        # trigger ProgramTransitionEventType and 
422
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
423
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
424
425
    #Transition
426
    async def ReadyToRunning(self):
427
        await self._current_state.write_value(
428
            self.localizedtext_running,
429
            varianttype=ua.VariantType.LocalizedText
430
            ) 
431
        await self._current_state_id.write_value(
432
            self._running_state.nodeid, 
433
            varianttype=ua.VariantType.NodeId
434
            )
435
        await self._last_transition.write_value(
436
            self.localizedtext_ready_to_running,
437
            varianttype=ua.VariantType.LocalizedText
438
            ) 
439
        await self._last_transition_id.write_value(
440
            self._ready_to_running.nodeid, 
441
            varianttype=ua.VariantType.NodeId
442
            )
443
        #FIXME 
444
        # trigger ProgramTransitionEventType and 
445
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
446
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
447
448
    #Transition
449
    async def RunningToHalted(self):
450
        await self._current_state.write_value(
451
            self.localizedtext_halted,
452
            varianttype=ua.VariantType.LocalizedText
453
            ) 
454
        await self._current_state_id.write_value(
455
            self._halted_state.nodeid, 
456
            varianttype=ua.VariantType.NodeId
457
            )
458
        await self._last_transition.write_value(
459
            self.localizedtext_running_to_halted,
460
            varianttype=ua.VariantType.LocalizedText
461
            ) 
462
        await self._last_transition_id.write_value(
463
            self._running_to_halted.nodeid, 
464
            varianttype=ua.VariantType.NodeId
465
            )
466
        #FIXME 
467
        # trigger ProgramTransitionEventType and 
468
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
469
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
470
471
    #Transition
472
    async def RunningToReady(self):
473
        await self._current_state.write_value(
474
            self.localizedtext_ready,
475
            varianttype=ua.VariantType.LocalizedText
476
            ) 
477
        await self._current_state_id.write_value(
478
            self._ready_state.nodeid, 
479
            varianttype=ua.VariantType.NodeId
480
            )
481
        await self._last_transition.write_value(
482
            self.localizedtext_running_to_ready,
483
            varianttype=ua.VariantType.LocalizedText
484
            ) 
485
        await self._last_transition_id.write_value(
486
            self._running_to_ready.nodeid, 
487
            varianttype=ua.VariantType.NodeId
488
            )
489
        #FIXME 
490
        # trigger ProgramTransitionEventType and 
491
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
492
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
493
494
    #Transition
495
    async def RunningToSuspended(self):
496
        await self._current_state.write_value(
497
            self.localizedtext_suspended,
498
            varianttype=ua.VariantType.LocalizedText
499
            ) 
500
        await self._current_state_id.write_value(
501
            self._suspended_state.nodeid, 
502
            varianttype=ua.VariantType.NodeId
503
            )
504
        await self._last_transition.write_value(
505
            self.localizedtext_running_to_suspended,
506
            varianttype=ua.VariantType.LocalizedText
507
            ) 
508
        await self._last_transition_id.write_value(
509
            self._running_to_suspended.nodeid, 
510
            varianttype=ua.VariantType.NodeId
511
            )
512
        #FIXME 
513
        # trigger ProgramTransitionEventType and 
514
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
515
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
516
517
    #Transition 
518
    async def SuspendedToRunning(self):
519
        await self._current_state.write_value(
520
            self.localizedtext_running,
521
            varianttype=ua.VariantType.LocalizedText
522
            ) 
523
        await self._current_state_id.write_value(
524
            self._running_state.nodeid, 
525
            varianttype=ua.VariantType.NodeId
526
            )
527
        await self._last_transition.write_value(
528
            self.localizedtext_suspended_to_running,
529
            varianttype=ua.VariantType.LocalizedText
530
            )
531
        await self._last_transition_id.write_value(
532
            self._suspended_to_running.nodeid, 
533
            varianttype=ua.VariantType.NodeId
534
            )
535
        #FIXME 
536
        # trigger ProgramTransitionEventType and 
537
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
538
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
539
540
    #Transition
541
    async def SuspendedToHalted(self):
542
        await self._current_state.write_value(
543
            self.localizedtext_halted,
544
            varianttype=ua.VariantType.LocalizedText
545
            ) 
546
        await self._current_state_id.write_value(
547
            self._halted_state.nodeid, 
548
            varianttype=ua.VariantType.NodeId
549
            )
550
        await self._last_transition.write_value(
551
            self.localizedtext_suspended_to_halted,
552
            varianttype=ua.VariantType.LocalizedText
553
            ) 
554
        await self._last_transition_id.write_value(
555
            self._suspended_to_halted.nodeid, 
556
            varianttype=ua.VariantType.NodeId
557
            )
558
        #FIXME 
559
        # trigger ProgramTransitionEventType and 
560
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
561
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
562
563
    #Transition
564
    async def SuspendedToReady(self):
565
        await self._current_state.write_value(
566
            self.localizedtext_ready,
567
            varianttype=ua.VariantType.LocalizedText
568
            ) 
569
        await self._current_state_id.write_value(
570
            self._ready_state.nodeid, 
571
            varianttype=ua.VariantType.NodeId
572
            )
573
        await self._last_transition.write_value(
574
            self.localizedtext_suspended_to_ready,
575
            varianttype=ua.VariantType.LocalizedText
576
            ) 
577
        await self._last_transition_id.write_value(
578
            self._suspended_to_ready.nodeid, 
579
            varianttype=ua.VariantType.NodeId
580
            )
581
        #FIXME 
582
        # trigger ProgramTransitionEventType and 
583
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
584
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
585
586
    #Transition 
587
    async def ReadyToHalted(self):
588
        await self._current_state.write_value(
589
            self.localizedtext_halted,
590
            varianttype=ua.VariantType.LocalizedText
591
            ) 
592
        await self._current_state_id.write_value(
593
            self._halted_state.nodeid, 
594
            varianttype=ua.VariantType.NodeId
595
            )
596
        await self._last_transition.write_value(
597
            self.localizedtext_ready_to_halted,
598
            varianttype=ua.VariantType.LocalizedText
599
            ) 
600
        await self._last_transition_id.write_value(
601
            self._ready_to_halted.nodeid, 
602
            varianttype=ua.VariantType.NodeId
603
            )
604
        #FIXME 
605
        # trigger ProgramTransitionEventType and 
606
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
607
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
608
609
    #method to be linked to uamethod
610
    async def Start(self):
611
        if await self._current_state.read_value() == self.localizedtext_ready:
612
            return await ReadyToRunning()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ReadyToRunning does not seem to be defined.
Loading history...
613
        else:
614
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
615
616
    #method to be linked to uamethod
617
    async def Suspend(self):
618
        if await self._current_state.read_value() == self.localizedtext_running:
619
            return await RunningToSuspended()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable RunningToSuspended does not seem to be defined.
Loading history...
620
        else:
621
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
622
623
    #method to be linked to uamethod
624
    async def Resume(self):
625
        if await self._current_state.read_value() == self.localizedtext_suspended:
626
            return await SuspendedToRunning()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable SuspendedToRunning does not seem to be defined.
Loading history...
627
        else:
628
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
629
630
    #method to be linked to uamethod
631
    async def Halt(self):
632
        if await self._current_state.read_value() == self.localizedtext_ready:
633
            return await ReadyToHalted()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ReadyToHalted does not seem to be defined.
Loading history...
634
        elif await self._current_state.read_value() == self.localizedtext_running:
635
            return await RunningToHalted()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable RunningToHalted does not seem to be defined.
Loading history...
636
        elif await self._current_state.read_value() == self.localizedtext_suspended:
637
            return await SuspendedToHalted()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable SuspendedToHalted does not seem to be defined.
Loading history...
638
        else:
639
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
640
641
    #method to be linked to uamethod
642
    async def Reset(self):
643
        if await self._current_state.read_value() == self.localizedtext_halted:
644
            return await HaltedToReady()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable HaltedToReady does not seem to be defined.
Loading history...
645
        else:
646
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
647
648
class ShelvedStateMachineTypeClass(FiniteStateMachineTypeClass):
649
    '''
650
    NOT IMPLEMENTED "ShelvedStateMachineType"
651
    '''
652
    def __init__(self, server=None, parent=None, idx=None, name=None):
653
        super().__init__(server, parent, idx, name)
654
        if name == None:
655
            name = "ShelvedStateMachine"
656
        self._state_machine_type = ua.NodeId(2929, 0)
657
        raise NotImplementedError
658
659
660
661
#FIXME REMOVE BEFOR MERGE
662
#Devtest / Workbench
663
if __name__ == "__main__":
664
    async def main():
665
        logging.basicConfig(level=logging.INFO)
666
        _logger = logging.getLogger('asyncua')
667
668
        server = Server()
669
        await server.init()
670
671
        idx = await server.register_namespace("http://testnamespace.org/UA")
672
673
        mystatemachine = StateMachineTypeClass(server, server.nodes.objects, idx, "StateMachine")
674
        await mystatemachine.install(optionals=True)
675
676
        state1 = mystatemachine.State("Idle", 1)
677
        await mystatemachine.add_state(state1)
678
        state2 = mystatemachine.State("Loading", 2)
679
        await mystatemachine.add_state(state2)
680
        state3 = mystatemachine.State("Initializing", 3)
681
        await mystatemachine.add_state(state3)
682
        state4 = mystatemachine.State("Processing", 4)
683
        await mystatemachine.add_state(state4)
684
        state5 = mystatemachine.State("Finished", 5)
685
        await mystatemachine.add_state(state5)
686
687
        trans1 = mystatemachine.Transition("to Idle", 1)
688
        await mystatemachine.add_transition(trans1)
689
        trans2 = mystatemachine.Transition("to Loading", 2)
690
        await mystatemachine.add_transition(trans2)
691
        trans3 = mystatemachine.Transition("to Initializing", 3)
692
        await mystatemachine.add_transition(trans3)
693
        trans4 = mystatemachine.Transition("to Processing", 4)
694
        await mystatemachine.add_transition(trans4)
695
        trans5 = mystatemachine.Transition("to Finished", 5)
696
        await mystatemachine.add_transition(trans5)
697
698
        await mystatemachine.change_state(state1, trans1, f"{mystatemachine._name}: Idle", 300)
699
700
        mystatemachine2 = StateMachineTypeClass(server, server.nodes.objects, idx, "StateMachine2")
701
        await mystatemachine2.install(optionals=False)
702
        sm2state1 = mystatemachine2.State("Idle", 1)
703
        await mystatemachine2.add_state(sm2state1)
704
        await mystatemachine2.change_state(sm2state1)
705
706
        async with server:
707
            while 1:
708
                await asyncio.sleep(2)
709
                await mystatemachine.change_state(state2, trans2, f"{mystatemachine._name}: Loading", 350)
710
                await asyncio.sleep(2)
711
                await mystatemachine.change_state(state3, trans3, f"{mystatemachine._name}: Initializing", 400)
712
                await asyncio.sleep(2)
713
                await mystatemachine.change_state(state4, trans4, f"{mystatemachine._name}: Processing", 600)
714
                await asyncio.sleep(2)
715
                await mystatemachine.change_state(state5, trans5, f"{mystatemachine._name}: Finished", 800)
716
                await asyncio.sleep(2)
717
                await mystatemachine.change_state(state1, trans1, f"{mystatemachine._name}: Idle", 500)
718
719
    asyncio.run(main())
720