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

asyncua.common.statemachine.StateMachine.install()   A

Complexity

Conditions 2

Size

Total Lines 25
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 18
nop 2
dl 0
loc 25
rs 9.5
c 0
b 0
f 0
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 StateMachine(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):
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
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable issub does not seem to be defined.
Loading history...
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 optionals
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(
118
                0, 
119
                "TransitionTime", 
120
                ua.Variant(datetime.datetime.utcnow(), varianttype=ua.VariantType.DateTime)
121
                )
122
            self._last_transition_effectivetransitiontime_node = await self._last_transition_node.add_property(
123
                0, 
124
                "EffectiveTransitionTime", 
125
                ua.Variant(datetime.datetime.utcnow(), varianttype=ua.VariantType.DateTime)
126
                )
127
        await self.init(self._state_machine_node)
128
    
129
    async def init(self, statemachine):
130
        '''
131
        initialize and get subnodes
132
        '''
133
        self._current_state_node = await statemachine.get_child(["CurrentState"])
134
        current_state_props = await self._current_state_node.get_properties()
135
        for prop in current_state_props:
136
            dn = await prop.read_display_name()
137
            if dn.Text == "Id":
138
                self._current_state_id_node = await self._current_state_node.get_child(["Id"])
139
            elif dn.Text == "Name":
140
                self._current_state_name_node = await self._current_state_node.get_child(["Name"])
141
            elif dn.Text == "Number":
142
                self._current_state_number_node = await self._current_state_node.get_child(["Number"])
143
            else:
144
                _logger.warning(f"{await statemachine.read_browse_name()} CurrentState Unknown propertie: {dn.Text}")
145
        if self._optionals:
146
            self._last_transition_node = await statemachine.get_child(["LastTransition"])
147
            last_transition_props = await self._last_transition_node.get_properties()
148
            for prop in last_transition_props:
149
                dn = await prop.read_display_name()
150
                if dn.Text == "Id":
151
                    self._last_transition_id_node = await self._last_transition_node.get_child(["Id"])
152
                elif dn.Text == "Name":
153
                    self._last_transition_name_node = await self._last_transition_node.get_child(["Name"])
154
                elif dn.Text == "Number":
155
                    self._last_transition_number_node = await self._last_transition_node.get_child(["Number"])
156
                elif dn.Text == "TransitionTime":
157
                    self._last_transition_transitiontime_node = await self._last_transition_node.get_child(["TransitionTime"])
158
                elif dn.Text == "EffectiveTransitionTime":
159
                    self._last_transition_effectivetransitiontime_node = await self._last_transition_node.get_child(["EffectiveTransitionTime"])
160
                else:
161
                    _logger.warning(f"{await statemachine.read_browse_name()} LastTransition Unknown propertie: {dn.Text}")
162
        self._evgen = await self._server.get_event_generator(self.evtype, self._state_machine_node)
163
164
    async def change_state(self, state, transition=None, event_msg=None, severity=500):
165
        '''
166
        method to change the state of the statemachine
167
        state: "self.State" mandatory
168
        transition: "self.Transition" optional
169
        event_msg: "string/LocalizedText" optional
170
        severity: "Int" optional
171
        '''
172
        await self._write_state(state)
173
        if transition:
174
            await self._write_transition(transition)
175
        if event_msg:
176
            if isinstance(event_msg, str):
177
                event_msg = ua.LocalizedText(event_msg, self.locale)
178
            self._evgen.event.Message = event_msg
179
            self._evgen.event.Severity = severity
180
            await self._evgen.trigger()
181
182
    async def _write_state(self, state):
183
        if not isinstance(state, self.State):
184
            raise ValueError
185
        await self._current_state_node.write_value(ua.LocalizedText(state.name, self.locale), ua.VariantType.LocalizedText)
186
        if state.node:
187
            if self._current_state_id_node:
188
                await self._current_state_id_node.write_value(state.node.nodeid, ua.VariantType.NodeId)
189
            if self._current_state_name_node:
190
                await self._current_state_name_node.write_value(state.name, ua.VariantType.QualifiedName)
191
            if self._current_state_number_node:
192
                await self._current_state_number_node.write_value(state.number, ua.VariantType.UInt32)
193
194
    async def _write_transition(self, transition):
195
        '''
196
        transition: self.Transition
197
        issub: boolean (true if it is a transition between substates)
198
        '''
199
        if not isinstance(transition, self.Transition):
200
            raise ValueError
201
        if transition.issub == False:
202
            transition._transitiontime = datetime.datetime.utcnow()
203
        transition._effectivetransitiontime = datetime.datetime.utcnow()
204
        await self._last_transition_node.write_value(ua.LocalizedText(transition.name, self.locale), ua.VariantType.LocalizedText)
205
        if self._optionals:
206
            if self._last_transition_id_node:
207
                await self._last_transition_id_node.write_value(transition.node.nodeid, ua.VariantType.NodeId)
208
            if self._last_transition_name_node:
209
                await self._last_transition_name_node.write_value(ua.QualifiedName(transition.name, self._idx), ua.VariantType.QualifiedName)
210
            if self._last_transition_number_node:
211
                await self._last_transition_number_node.write_value(transition.number, ua.VariantType.UInt32)
212
            if self._last_transition_transitiontime_node:
213
                await self._last_transition_transitiontime_node.write_value(transition._transitiontime, ua.VariantType.DateTime)
214
            if self._last_transition_effectivetransitiontime_node:
215
                await self._last_transition_effectivetransitiontime_node.write_value(transition._effectivetransitiontime, ua.VariantType.DateTime)
216
            
217
    async def add_state(self, state, state_type=ua.NodeId(2307, 0), optionals=False):
218
        '''
219
        this method adds a state object to the statemachines address space
220
        state: self.State,
221
        InitialStateType: ua.NodeId(2309, 0),
222
        StateType: ua.NodeId(2307, 0),
223
        ChoiceStateType: ua.NodeId(15109,0),
224
        '''
225
        if not isinstance(state, self.State):
226
            raise ValueError
227
        if not state_type in [ua.NodeId(2309, 0),ua.NodeId(2307, 0),ua.NodeId(15109,0)]:
228
            # unknown state type!
229
            raise ValueError
230
        state.node = await self._state_machine_node.add_object(
231
            self._idx, 
232
            state.name, 
233
            objecttype=state_type, 
234
            instantiate_optional=optionals
235
            )
236
        state_number = await state.node.get_child(["StateNumber"])
237
        await state_number.write_value(state.number, ua.VariantType.UInt32)
238
        return state.node
239
240
    async def add_transition(self, transition, transition_type=ua.NodeId(2310, 0), optionals=False):
241
        '''
242
        this method adds a transition object to the statemachines address space
243
        transition: self.Transition,
244
        transition_type: ua.NodeId(2310, 0),
245
        '''
246
        if not isinstance(transition, self.Transition):
247
            raise ValueError
248
        transition.node = await self._state_machine_node.add_object(
249
            self._idx, 
250
            transition.name, 
251
            objecttype=transition_type, 
252
            instantiate_optional=optionals
253
            )
254
        transition_number = await transition.node.get_child(["TransitionNumber"])
255
        await transition_number.write_value(transition.number, ua.VariantType.UInt32)
256
        return transition.node
257
258
    async def remove(self, nodes):
259
        #FIXME Is it wise to remove a part of a program/statemachine dynamically? at this point i am not sure
260
        raise NotImplementedError
261
262
class FiniteStateMachine(StateMachine):
263
    '''
264
    Implementation of an FiniteStateMachineType a little more advanced than the basic one
265
    if you need to know the available states and transition from clientside
266
    '''
267
    def __init__(self, server=None, parent=None, idx=None, name=None):
268
        super().__init__(server, parent, idx, name)
269
        if name == None:
270
            name = "FiniteStateMachine"
271
        self._state_machine_type = ua.NodeId(2771, 0)
272
        self._available_states_node = None
273
        self._available_transitions_node = None
274
275
    async def set_available_states(self, states):
276
        if not self._available_states_node:
277
            self._available_states_node = await self._state_machine_node.get_child(["AvailableStates"])
278
        if isinstance(states, list):
279
            return await self._available_states_node.write_value(states, varianttype=ua.VariantType.NodeId)
280
        return ValueError
281
282
    async def set_available_transitions(self, transitions):
283
        if self._optionals:
284
            if not self._available_transitions_node:
285
                self._available_transitions_node = await self._state_machine_node.get_child(["AvailableTransitions"])
286
            if isinstance(transitions, list):
287
                await self._available_transitions_node.write_value(transitions, varianttype=ua.VariantType.NodeId)
288
            return ValueError
289
290
    async def find_all_states(self):
291
        return NotImplementedError
292
    
293
    async def find_all_transitions(self):
294
        return NotImplementedError
295
296
class ExclusiveLimitStateMachine(FiniteStateMachine):
297
    '''
298
    NOT IMPLEMENTED "ExclusiveLimitStateMachineType"
299
    '''
300
    def __init__(self, server=None, parent=None, idx=None, name=None):
301
        super().__init__(server, parent, idx, name)
302
        if name == None:
303
            name = "ExclusiveLimitStateMachine"
304
        self._state_machine_type = ua.NodeId(9318, 0)
305
        raise NotImplementedError
306
307
class FileTransferStateMachine(FiniteStateMachine):
308
    '''
309
    NOT IMPLEMENTED "FileTransferStateMachineType"
310
    '''
311
    def __init__(self, server=None, parent=None, idx=None, name=None):
312
        super().__init__(server, parent, idx, name)
313
        if name == None:
314
            name = "FileTransferStateMachine"
315
        self._state_machine_type = ua.NodeId(15803, 0)
316
        raise NotImplementedError
317
318
class ProgramStateMachine(FiniteStateMachine):
319
    '''
320
    https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.3/
321
    Implementation of an ProgramStateMachine its quite a complex statemachine with the 
322
    optional possibility to make the statchange from clientside via opcua-methods
323
    '''
324
    def __init__(self, server=None, parent=None, idx=None, name=None):
325
        super().__init__(server, parent, idx, name)
326
        if name == None:
327
            name = "ProgramStateMachine"
328
        self._state_machine_type = ua.NodeId(2391, 0)
329
        self.evtype = ProgramTransitionEvent()
330
331
        # 5.2.3.2 ProgramStateMachineType states
332
        self._ready_state_node = None #State node
333
        self._halted_state_node = None #State node
334
        self._running_state_node = None #State node
335
        self._suspended_state_node = None #State node
336
337
        # 5.2.3.3 ProgramStateMachineType transitions
338
        self._halted_to_ready_node = None #Transition node
339
        self._ready_to_running_node = None #Transition node
340
        self._running_to_halted_node = None #Transition node
341
        self._running_to_ready_node = None #Transition node
342
        self._running_to_suspended_node = None #Transition node
343
        self._suspended_to_running_node = None #Transition node
344
        self._suspended_to_halted_node = None #Transition node
345
        self._suspended_to_ready_node = None #Transition node
346
        self._ready_to_halted_node = None #Transition node
347
348
        # 5.2.3.2 ProgramStateMachineType states
349
        self._halted_state_id_node = None #State property (StateNumber value 11)
350
        self._ready_state_id_node = None #State property (StateNumber value 12)
351
        self._running_state_id_node = None #State property (StateNumber value 13)
352
        self._suspended_state_id_node = None #State property (StateNumber value 14)
353
354
        # 5.2.3.3 ProgramStateMachineType transitions
355
        self._halted_to_ready_id_node = None #Transition property (TransitionNumber value 1)
356
        self._ready_to_running_id_node = None #Transition property (TransitionNumber value 2)
357
        self._running_to_halted_id_node = None #Transition property (TransitionNumber value 3)
358
        self._running_to_ready_id_node = None #Transition property (TransitionNumber value 4)
359
        self._running_to_suspended_id_node = None #Transition property (TransitionNumber value 5)
360
        self._suspended_to_running_id_node = None #Transition property (TransitionNumber value 6)
361
        self._suspended_to_halted_id_node = None #Transition property (TransitionNumber value 7)
362
        self._suspended_to_ready_id_node = None #Transition property (TransitionNumber value 8)
363
        self._ready_to_halted_id_node = None #Transition property (TransitionNumber value 9)
364
365
        # 4.2.7 Program Control Methods (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.7/)
366
        self._halt_method_node = None #uamethod node
367
        self._reset_method_node = None #uamethod node
368
        self._resume_method_node = None #uamethod node
369
        self._start_method_node = None #uamethod node
370
        self._suspend_method_node = None #uamethod node
371
372
        #can be overwritten if you want a different language
373
        self.localizedtext_ready = ua.LocalizedText("Ready", "en-US")
374
        self.localizedtext_running = ua.LocalizedText("Running", "en-US")
375
        self.localizedtext_halted = ua.LocalizedText("Halted", "en-US")
376
        self.localizedtext_suspended= ua.LocalizedText("Suspended", "en-US")
377
        self.localizedtext_halted_to_ready = ua.LocalizedText("HaltedToReady", "en-US")
378
        self.localizedtext_ready_to_running = ua.LocalizedText("ReadyToRunning", "en-US")
379
        self.localizedtext_running_to_halted = ua.LocalizedText("RunningToHalted", "en-US")
380
        self.localizedtext_running_to_ready = ua.LocalizedText("RunningToReady", "en-US")
381
        self.localizedtext_running_to_suspended = ua.LocalizedText("RunningToSuspended", "en-US")
382
        self.localizedtext_suspended_to_running = ua.LocalizedText("SuspendedToRunning", "en-US")
383
        self.localizedtext_suspended_to_halted = ua.LocalizedText("SuspendedToHalted", "en-US")
384
        self.localizedtext_suspended_to_ready = ua.LocalizedText("SuspendedToReady", "en-US")
385
        self.localizedtext_ready_to_halted = ua.LocalizedText("ReadyToHalted", "en-US")
386
387
    async def install(self, optionals=False):
388
        '''
389
        setup adressspace and initialize 
390
        '''
391
        self._optionals = optionals
392
        self._state_machine_node = await self._parent.add_object(
393
            self._idx, 
394
            self._name, 
395
            objecttype=self._state_machine_type, 
396
            instantiate_optional=optionals
397
            )
398
        #FIXME get children and map children
399
400
    #Transition
401
    async def HaltedToReady(self):
402
        await self._current_state_node.write_value(
403
            self.localizedtext_ready,
404
            varianttype=ua.VariantType.LocalizedText
405
            ) 
406
        await self._current_state_id_node.write_value(
407
            self._ready_state.nodeid, 
408
            varianttype=ua.VariantType.NodeId
409
            )
410
        await self._last_transition_node.write_value(
411
            self.localizedtext_halted_to_ready,
412
            varianttype=ua.VariantType.LocalizedText
413
            ) 
414
        await self._last_transition_id_node.write_value(
415
            self._halted_to_ready.nodeid,
416
            varianttype=ua.VariantType.NodeId
417
            )
418
        #FIXME 
419
        # trigger ProgramTransitionEventType and 
420
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
421
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
422
423
    #Transition
424
    async def ReadyToRunning(self):
425
        await self._current_state_node.write_value(
426
            self.localizedtext_running,
427
            varianttype=ua.VariantType.LocalizedText
428
            ) 
429
        await self._current_state_id_node.write_value(
430
            self._running_state.nodeid, 
431
            varianttype=ua.VariantType.NodeId
432
            )
433
        await self._last_transition_node.write_value(
434
            self.localizedtext_ready_to_running,
435
            varianttype=ua.VariantType.LocalizedText
436
            ) 
437
        await self._last_transition_id_node.write_value(
438
            self._ready_to_running.nodeid, 
439
            varianttype=ua.VariantType.NodeId
440
            )
441
        #FIXME 
442
        # trigger ProgramTransitionEventType and 
443
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
444
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
445
446
    #Transition
447
    async def RunningToHalted(self):
448
        await self._current_state_node.write_value(
449
            self.localizedtext_halted,
450
            varianttype=ua.VariantType.LocalizedText
451
            ) 
452
        await self._current_state_id_node.write_value(
453
            self._halted_state.nodeid, 
454
            varianttype=ua.VariantType.NodeId
455
            )
456
        await self._last_transition_node.write_value(
457
            self.localizedtext_running_to_halted,
458
            varianttype=ua.VariantType.LocalizedText
459
            ) 
460
        await self._last_transition_id_node.write_value(
461
            self._running_to_halted.nodeid, 
462
            varianttype=ua.VariantType.NodeId
463
            )
464
        #FIXME 
465
        # trigger ProgramTransitionEventType and 
466
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
467
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
468
469
    #Transition
470
    async def RunningToReady(self):
471
        await self._current_state_node.write_value(
472
            self.localizedtext_ready,
473
            varianttype=ua.VariantType.LocalizedText
474
            ) 
475
        await self._current_state_id_node.write_value(
476
            self._ready_state.nodeid, 
477
            varianttype=ua.VariantType.NodeId
478
            )
479
        await self._last_transition_node.write_value(
480
            self.localizedtext_running_to_ready,
481
            varianttype=ua.VariantType.LocalizedText
482
            ) 
483
        await self._last_transition_id_node.write_value(
484
            self._running_to_ready.nodeid, 
485
            varianttype=ua.VariantType.NodeId
486
            )
487
        #FIXME 
488
        # trigger ProgramTransitionEventType and 
489
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
490
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
491
492
    #Transition
493
    async def RunningToSuspended(self):
494
        await self._current_state_node.write_value(
495
            self.localizedtext_suspended,
496
            varianttype=ua.VariantType.LocalizedText
497
            ) 
498
        await self._current_state_id_node.write_value(
499
            self._suspended_state.nodeid, 
500
            varianttype=ua.VariantType.NodeId
501
            )
502
        await self._last_transition_node.write_value(
503
            self.localizedtext_running_to_suspended,
504
            varianttype=ua.VariantType.LocalizedText
505
            ) 
506
        await self._last_transition_id_node.write_value(
507
            self._running_to_suspended.nodeid, 
508
            varianttype=ua.VariantType.NodeId
509
            )
510
        #FIXME 
511
        # trigger ProgramTransitionEventType and 
512
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
513
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
514
515
    #Transition 
516
    async def SuspendedToRunning(self):
517
        await self._current_state_node.write_value(
518
            self.localizedtext_running,
519
            varianttype=ua.VariantType.LocalizedText
520
            ) 
521
        await self._current_state_id_node.write_value(
522
            self._running_state.nodeid, 
523
            varianttype=ua.VariantType.NodeId
524
            )
525
        await self._last_transition_node.write_value(
526
            self.localizedtext_suspended_to_running,
527
            varianttype=ua.VariantType.LocalizedText
528
            )
529
        await self._last_transition_id_node.write_value(
530
            self._suspended_to_running.nodeid, 
531
            varianttype=ua.VariantType.NodeId
532
            )
533
        #FIXME 
534
        # trigger ProgramTransitionEventType and 
535
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
536
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
537
538
    #Transition
539
    async def SuspendedToHalted(self):
540
        await self._current_state_node.write_value(
541
            self.localizedtext_halted,
542
            varianttype=ua.VariantType.LocalizedText
543
            ) 
544
        await self._current_state_id_node.write_value(
545
            self._halted_state.nodeid, 
546
            varianttype=ua.VariantType.NodeId
547
            )
548
        await self._last_transition_node.write_value(
549
            self.localizedtext_suspended_to_halted,
550
            varianttype=ua.VariantType.LocalizedText
551
            ) 
552
        await self._last_transition_id_node.write_value(
553
            self._suspended_to_halted.nodeid, 
554
            varianttype=ua.VariantType.NodeId
555
            )
556
        #FIXME 
557
        # trigger ProgramTransitionEventType and 
558
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
559
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
560
561
    #Transition
562
    async def SuspendedToReady(self):
563
        await self._current_state_node.write_value(
564
            self.localizedtext_ready,
565
            varianttype=ua.VariantType.LocalizedText
566
            ) 
567
        await self._current_state_id_node.write_value(
568
            self._ready_state.nodeid, 
569
            varianttype=ua.VariantType.NodeId
570
            )
571
        await self._last_transition_node.write_value(
572
            self.localizedtext_suspended_to_ready,
573
            varianttype=ua.VariantType.LocalizedText
574
            ) 
575
        await self._last_transition_id_node.write_value(
576
            self._suspended_to_ready.nodeid, 
577
            varianttype=ua.VariantType.NodeId
578
            )
579
        #FIXME 
580
        # trigger ProgramTransitionEventType and 
581
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
582
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
583
584
    #Transition 
585
    async def ReadyToHalted(self):
586
        await self._current_state_node.write_value(
587
            self.localizedtext_halted,
588
            varianttype=ua.VariantType.LocalizedText
589
            ) 
590
        await self._current_state_id_node.write_value(
591
            self._halted_state.nodeid, 
592
            varianttype=ua.VariantType.NodeId
593
            )
594
        await self._last_transition_node.write_value(
595
            self.localizedtext_ready_to_halted,
596
            varianttype=ua.VariantType.LocalizedText
597
            ) 
598
        await self._last_transition_id_node.write_value(
599
            self._ready_to_halted.nodeid, 
600
            varianttype=ua.VariantType.NodeId
601
            )
602
        #FIXME 
603
        # trigger ProgramTransitionEventType and 
604
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
605
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
606
607
    #method to be linked to uamethod
608
    async def Start(self):
609
        if await self._current_state_node.read_value() == self.localizedtext_ready:
610
            return await ReadyToRunning()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ReadyToRunning does not seem to be defined.
Loading history...
611
        else:
612
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
613
614
    #method to be linked to uamethod
615
    async def Suspend(self):
616
        if await self._current_state_node.read_value() == self.localizedtext_running:
617
            return await RunningToSuspended()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable RunningToSuspended does not seem to be defined.
Loading history...
618
        else:
619
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
620
621
    #method to be linked to uamethod
622
    async def Resume(self):
623
        if await self._current_state_node.read_value() == self.localizedtext_suspended:
624
            return await SuspendedToRunning()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable SuspendedToRunning does not seem to be defined.
Loading history...
625
        else:
626
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
627
628
    #method to be linked to uamethod
629
    async def Halt(self):
630
        val = await self._current_state_node.read_value()
631
        if val == self.localizedtext_ready:
632
            return await ReadyToHalted()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ReadyToHalted does not seem to be defined.
Loading history...
633
        elif val == self.localizedtext_running:
634
            return await RunningToHalted()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable RunningToHalted does not seem to be defined.
Loading history...
635
        elif val == self.localizedtext_suspended:
636
            return await SuspendedToHalted()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable SuspendedToHalted does not seem to be defined.
Loading history...
637
        else:
638
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
639
640
    #method to be linked to uamethod
641
    async def Reset(self):
642
        if await self._current_state_node.read_value() == self.localizedtext_halted:
643
            return await HaltedToReady()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable HaltedToReady does not seem to be defined.
Loading history...
644
        else:
645
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
646
647
class ShelvedStateMachine(FiniteStateMachine):
648
    '''
649
    NOT IMPLEMENTED "ShelvedStateMachineType"
650
    '''
651
    def __init__(self, server=None, parent=None, idx=None, name=None):
652
        super().__init__(server, parent, idx, name)
653
        if name == None:
654
            name = "ShelvedStateMachine"
655
        self._state_machine_type = ua.NodeId(2929, 0)
656
        raise NotImplementedError
657