Passed
Pull Request — master (#348)
by
unknown
04:01 queued 01:12
created

StateMachineTypeClass.init()   D

Complexity

Conditions 12

Size

Total Lines 34
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 29
nop 2
dl 0
loc 34
rs 4.8
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like asyncua.common.statemachine.StateMachineTypeClass.init() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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