Passed
Pull Request — master (#348)
by
unknown
03:01
created

ProgramStateMachineTypeClass.Resume()   A

Complexity

Conditions 2

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nop 1
dl 0
loc 5
rs 10
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
5
Basic statemachines discribed in OPC UA Spec.:
6
StateMachineType
7
FiniteStateMachineType
8
ExclusiveLimitStateMachineType
9
FileTransferStateMachineType
10
ProgramStateMachineType
11
ShelvedStateMachineType
12
13
Relevant information:
14
Overview - https://reference.opcfoundation.org/v104/Core/docs/Part10/5.2.3/#5.2.3.1
15
States - https://reference.opcfoundation.org/v104/Core/docs/Part10/5.2.3/#5.2.3.2
16
Transitions - https://reference.opcfoundation.org/v104/Core/docs/Part10/5.2.3/#5.2.3.3
17
Events - https://reference.opcfoundation.org/v104/Core/docs/Part10/5.2.5/
18
'''
19
import asyncio, logging
20
21
#FIXME 
22
# -change to relativ imports!
23
# -remove unused imports
24
from asyncua import Server, ua, Node
25
from asyncua.common.instantiate_util import instantiate
26
27
class StateMachineTypeClass(object):
28
    '''
29
    Implementation of an StateMachineType (most basic type)
30
    '''
31
    def __init__(self, server=None, parent=None, idx=None, name=None):
32
        if not server: raise ValueError #FIXME change to check instance type
33
        if not parent: raise ValueError #FIXME change to check instance type
34
        if idx == None:
35
            idx = parent.nodeid.NamespaceIndex
36
        if name == None:
37
            name = "StateMachine"
38
        self._server = server
39
        self._parent = parent
40
        self._state_machine_node = None
41
        self._state_machine_type = ua.NodeId(2299, 0)
42
        self._name = name
43
        self._idx = idx
44
        self._optionals = False
45
        self._current_state_node = None
46
        self._current_state_id_node = None
47
        self._last_transition_node = None
48
        self._last_transition_node_id = None
49
50
    async def install(self, optionals=False):
51
        '''
52
        setup adressspace and initialize 
53
        '''
54
        self._optionals = optionals
55
        self._state_machine_node = await self._parent.add_object(
56
            self._idx, 
57
            self._name, 
58
            objecttype=self._state_machine_type, 
59
            instantiate_optional=optionals
60
            )
61
        self._current_state_node = await self._state_machine_node.get_child(["CurrentState"])
62
        self._current_state_id_node = await self._state_machine_node.get_child(["CurrentState","Id"])
63
        if self._optionals:
64
            self._current_transition_node = await self._state_machine_node.get_child(["LastTransition"])
65
            self._current_transition_id_node = await self._state_machine_node.get_child(["LastTransition","Id"])
66
67
        #FIXME initialise values
68
        '''
69
        maybe its smart to check parents children for a initial state instance (InitialStateType) 
70
        and initialize it with its id but if no state instance is provided ... i will log a 
71
        warning
72
        '''
73
74
    async def change_state(self, state_name, state_node, transition_name=None, transition_node=None):
75
        '''
76
        method to change the state of the statemachine
77
        state_name: ua.LocalizedText()
78
        state: ua.NodeId() <- StateType node
79
        transition_name: ua.LocalizedText()
80
        transition: ua.NodeId() <- TransitionType node
81
        '''
82
        #FIXME check StateType exist
83
        #FIXME check TransitionTypeType exist
84
        await self.write_state(state_name, state_node)
85
        if self._optionals and transition_name and transition_node:
86
            await self.write_transition(transition_name, transition_node)
87
        #FIXME trigger TransitionEventType
88
89
    async def write_state(self, state_name, state_node):
90
        #FIXME check types/class
91
        await self._current_state_node.write_value(state_name)
92
        await self._current_state_id_node.write_value(state_node)
93
94
    async def write_transition(self, transition_name, transition_node):
95
        #FIXME check types/class
96
        await self._last_transition_node.write_value(transition_name)
97
        await self._last_transition_id_node.write_value(transition_node)
98
    
99
    async def add_state(self, name, state_type, optionals):
100
        #FIXME check types/class
101
        return await self._state_machine_node.add_object(
102
            self._idx, 
103
            name, 
104
            objecttype=state_type, 
105
            instantiate_optional=optionals
106
            )
107
108
    async def add_transition(self, name, transition_type, optionals):
109
        #FIXME check types/class
110
        return await self._state_machine_node.add_object(
111
            self._idx, 
112
            name, 
113
            objecttype=transition_type, 
114
            instantiate_optional=optionals
115
            )
116
117
class FiniteStateMachineTypeClass(StateMachineTypeClass):
118
    '''
119
    Implementation of an FiniteStateMachineType a little more advanced than the basic one
120
    if you need to know the avalible states and transition from clientside
121
    '''
122
    def __init__(self, server=None, parent=None, idx=None, name=None):
123
        super().__init__(server, parent, idx, name)
124
        if name == None:
125
            name = "FiniteStateMachine"
126
        self._state_machine_type = ua.NodeId(2771, 0)
127
        self._avalible_states_node = None
128
        self._avalible_transitions_node = None
129
130
    async def install(self, avalible_states, avalible_transitions, optionals=False):
131
        '''
132
        setup adressspace and initialize 
133
        '''
134
        self._optionals = optionals
135
        self._state_machine_node = await self._parent.add_object(
136
            self._idx, 
137
            self._name, 
138
            objecttype=self._state_machine_type, 
139
            instantiate_optional=optionals
140
            )
141
        #FIXME get children and map children
142
        await self.set_avalible_states(avalible_states)
143
        await self.set_avalible_transitions(avalible_transitions)
144
145
    async def set_avalible_states(self, states):
146
        #check if its list
147
        await self._avalible_states_node.write_value(states, varianttype=ua.VariantType.NodeId)
148
149
    async def set_avalible_transitions(self, transitions):
150
        await self._avalible_transitions_node.write_value(transitions, varianttype=ua.VariantType.NodeId)
151
152
class ExclusiveLimitStateMachineTypeClass(FiniteStateMachineTypeClass):
153
    '''
154
    NOT IMPLEMENTED "ExclusiveLimitStateMachineType"
155
    '''
156
    def __init__(self, server=None, parent=None, idx=None, name=None):
157
        super().__init__(server, parent, idx, name)
158
        if name == None:
159
            name = "ExclusiveLimitStateMachine"
160
        self._state_machine_type = ua.NodeId(9318, 0)
161
        raise NotImplementedError
162
163
class FileTransferStateMachineTypeClass(FiniteStateMachineTypeClass):
164
    '''
165
    NOT IMPLEMENTED "FileTransferStateMachineType"
166
    '''
167
    def __init__(self, server=None, parent=None, idx=None, name=None):
168
        super().__init__(server, parent, idx, name)
169
        if name == None:
170
            name = "FileTransferStateMachine"
171
        self._state_machine_type = ua.NodeId(15803, 0)
172
        raise NotImplementedError
173
174
class ProgramStateMachineTypeClass(FiniteStateMachineTypeClass):
175
    '''
176
    https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.3/
177
    Implementation of an ProgramStateMachine its quite a complex statemachine with the 
178
    optional possibility to make the statchange from clientside via opcua-methods
179
    '''
180
    def __init__(self, server=None, parent=None, idx=None, name=None):
181
        super().__init__(server, parent, idx, name)
182
        if name == None:
183
            name = "ProgramStateMachine"
184
        self._state_machine_type = ua.NodeId(2391, 0)
185
186
        # 5.2.3.2 ProgramStateMachineType states
187
        self._ready_state_node = None #State node
188
        self._halted_state_node = None #State node
189
        self._running_state_node = None #State node
190
        self._suspended_state_node = None #State node
191
192
        # 5.2.3.3 ProgramStateMachineType transitions
193
        self._halted_to_ready_node = None #Transition node
194
        self._ready_to_running_node = None #Transition node
195
        self._running_to_halted_node = None #Transition node
196
        self._running_to_ready_node = None #Transition node
197
        self._running_to_suspended_node = None #Transition node
198
        self._suspended_to_running_node = None #Transition node
199
        self._suspended_to_halted_node = None #Transition node
200
        self._suspended_to_ready_node = None #Transition node
201
        self._ready_to_halted_node = None #Transition node
202
203
        # 5.2.3.2 ProgramStateMachineType states
204
        self._halted_state_id_node = None #State property (StateNumber value 11)
205
        self._ready_state_id_node = None #State property (StateNumber value 12)
206
        self._running_state_id_node = None #State property (StateNumber value 13)
207
        self._suspended_state_id_node = None #State property (StateNumber value 14)
208
209
        # 5.2.3.3 ProgramStateMachineType transitions
210
        self._halted_to_ready_id_node = None #Transition property (TransitionNumber value 1)
211
        self._ready_to_running_id_node = None #Transition property (TransitionNumber value 2)
212
        self._running_to_halted_id_node = None #Transition property (TransitionNumber value 3)
213
        self._running_to_ready_id_node = None #Transition property (TransitionNumber value 4)
214
        self._running_to_suspended_id_node = None #Transition property (TransitionNumber value 5)
215
        self._suspended_to_running_id_node = None #Transition property (TransitionNumber value 6)
216
        self._suspended_to_halted_id_node = None #Transition property (TransitionNumber value 7)
217
        self._suspended_to_ready_id_node = None #Transition property (TransitionNumber value 8)
218
        self._ready_to_halted_id_node = None #Transition property (TransitionNumber value 9)
219
220
        # 4.2.7 Program Control Methods (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.7/)
221
        self._halt_method_node = None #uamethod node
222
        self._reset_method_node = None #uamethod node
223
        self._resume_method_node = None #uamethod node
224
        self._start_method_node = None #uamethod node
225
        self._suspend_method_node = None #uamethod node
226
227
        #can be overwritten if you want a different language
228
        self.localizedtext_ready = ua.LocalizedText("Ready", "en-US")
229
        self.localizedtext_running = ua.LocalizedText("Running", "en-US")
230
        self.localizedtext_halted = ua.LocalizedText("Halted", "en-US")
231
        self.localizedtext_suspended= ua.LocalizedText("Suspended", "en-US")
232
        self.localizedtext_halted_to_ready = ua.LocalizedText("HaltedToReady", "en-US")
233
        self.localizedtext_ready_to_running = ua.LocalizedText("ReadyToRunning", "en-US")
234
        self.localizedtext_running_to_halted = ua.LocalizedText("RunningToHalted", "en-US")
235
        self.localizedtext_running_to_ready = ua.LocalizedText("RunningToReady", "en-US")
236
        self.localizedtext_running_to_suspended = ua.LocalizedText("RunningToSuspended", "en-US")
237
        self.localizedtext_suspended_to_running = ua.LocalizedText("SuspendedToRunning", "en-US")
238
        self.localizedtext_suspended_to_halted = ua.LocalizedText("SuspendedToHalted", "en-US")
239
        self.localizedtext_suspended_to_ready = ua.LocalizedText("SuspendedToReady", "en-US")
240
        self.localizedtext_ready_to_halted = ua.LocalizedText("ReadyToHalted", "en-US")
241
242
    async def install(self, optionals=False):
243
        '''
244
        setup adressspace and initialize 
245
        '''
246
        self._optionals = optionals
247
        self._state_machine_node = await self._parent.add_object(
248
            self._idx, 
249
            self._name, 
250
            objecttype=self._state_machine_type, 
251
            instantiate_optional=optionals
252
            )
253
        #FIXME get children and map children
254
255
    #Transition
256
    async def HaltedToReady(self):
257
        await self._current_state.write_value(
258
            self.localizedtext_ready,
259
            varianttype=ua.VariantType.LocalizedText
260
            ) 
261
        await self._current_state_id.write_value(
262
            self._ready_state.nodeid, 
263
            varianttype=ua.VariantType.NodeId
264
            )
265
        await self._last_transition.write_value(
266
            self.localizedtext_halted_to_ready,
267
            varianttype=ua.VariantType.LocalizedText
268
            ) 
269
        await self._last_transition_id.write_value(
270
            self._halted_to_ready.nodeid,
271
            varianttype=ua.VariantType.NodeId
272
            )
273
        #FIXME 
274
        # trigger ProgramTransitionEventType and 
275
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
276
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
277
278
    #Transition
279
    async def ReadyToRunning(self):
280
        await self._current_state.write_value(
281
            self.localizedtext_running,
282
            varianttype=ua.VariantType.LocalizedText
283
            ) 
284
        await self._current_state_id.write_value(
285
            self._running_state.nodeid, 
286
            varianttype=ua.VariantType.NodeId
287
            )
288
        await self._last_transition.write_value(
289
            self.localizedtext_ready_to_running,
290
            varianttype=ua.VariantType.LocalizedText
291
            ) 
292
        await self._last_transition_id.write_value(
293
            self._ready_to_running.nodeid, 
294
            varianttype=ua.VariantType.NodeId
295
            )
296
        #FIXME 
297
        # trigger ProgramTransitionEventType and 
298
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
299
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
300
301
    #Transition
302
    async def RunningToHalted(self):
303
        await self._current_state.write_value(
304
            self.localizedtext_halted,
305
            varianttype=ua.VariantType.LocalizedText
306
            ) 
307
        await self._current_state_id.write_value(
308
            self._halted_state.nodeid, 
309
            varianttype=ua.VariantType.NodeId
310
            )
311
        await self._last_transition.write_value(
312
            self.localizedtext_running_to_halted,
313
            varianttype=ua.VariantType.LocalizedText
314
            ) 
315
        await self._last_transition_id.write_value(
316
            self._running_to_halted.nodeid, 
317
            varianttype=ua.VariantType.NodeId
318
            )
319
        #FIXME 
320
        # trigger ProgramTransitionEventType and 
321
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
322
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
323
324
    #Transition
325
    async def RunningToReady(self):
326
        await self._current_state.write_value(
327
            self.localizedtext_ready,
328
            varianttype=ua.VariantType.LocalizedText
329
            ) 
330
        await self._current_state_id.write_value(
331
            self._ready_state.nodeid, 
332
            varianttype=ua.VariantType.NodeId
333
            )
334
        await self._last_transition.write_value(
335
            self.localizedtext_running_to_ready,
336
            varianttype=ua.VariantType.LocalizedText
337
            ) 
338
        await self._last_transition_id.write_value(
339
            self._running_to_ready.nodeid, 
340
            varianttype=ua.VariantType.NodeId
341
            )
342
        #FIXME 
343
        # trigger ProgramTransitionEventType and 
344
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
345
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
346
347
    #Transition
348
    async def RunningToSuspended(self):
349
        await self._current_state.write_value(
350
            self.localizedtext_suspended,
351
            varianttype=ua.VariantType.LocalizedText
352
            ) 
353
        await self._current_state_id.write_value(
354
            self._suspended_state.nodeid, 
355
            varianttype=ua.VariantType.NodeId
356
            )
357
        await self._last_transition.write_value(
358
            self.localizedtext_running_to_suspended,
359
            varianttype=ua.VariantType.LocalizedText
360
            ) 
361
        await self._last_transition_id.write_value(
362
            self._running_to_suspended.nodeid, 
363
            varianttype=ua.VariantType.NodeId
364
            )
365
        #FIXME 
366
        # trigger ProgramTransitionEventType and 
367
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
368
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
369
370
    #Transition 
371
    async def SuspendedToRunning(self):
372
        await self._current_state.write_value(
373
            self.localizedtext_running,
374
            varianttype=ua.VariantType.LocalizedText
375
            ) 
376
        await self._current_state_id.write_value(
377
            self._running_state.nodeid, 
378
            varianttype=ua.VariantType.NodeId
379
            )
380
        await self._last_transition.write_value(
381
            self.localizedtext_suspended_to_running,
382
            varianttype=ua.VariantType.LocalizedText
383
            )
384
        await self._last_transition_id.write_value(
385
            self._suspended_to_running.nodeid, 
386
            varianttype=ua.VariantType.NodeId
387
            )
388
        #FIXME 
389
        # trigger ProgramTransitionEventType and 
390
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
391
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
392
393
    #Transition
394
    async def SuspendedToHalted(self):
395
        await self._current_state.write_value(
396
            self.localizedtext_halted,
397
            varianttype=ua.VariantType.LocalizedText
398
            ) 
399
        await self._current_state_id.write_value(
400
            self._halted_state.nodeid, 
401
            varianttype=ua.VariantType.NodeId
402
            )
403
        await self._last_transition.write_value(
404
            self.localizedtext_suspended_to_halted,
405
            varianttype=ua.VariantType.LocalizedText
406
            ) 
407
        await self._last_transition_id.write_value(
408
            self._suspended_to_halted.nodeid, 
409
            varianttype=ua.VariantType.NodeId
410
            )
411
        #FIXME 
412
        # trigger ProgramTransitionEventType and 
413
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
414
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
415
416
    #Transition
417
    async def SuspendedToReady(self):
418
        await self._current_state.write_value(
419
            self.localizedtext_ready,
420
            varianttype=ua.VariantType.LocalizedText
421
            ) 
422
        await self._current_state_id.write_value(
423
            self._ready_state.nodeid, 
424
            varianttype=ua.VariantType.NodeId
425
            )
426
        await self._last_transition.write_value(
427
            self.localizedtext_suspended_to_ready,
428
            varianttype=ua.VariantType.LocalizedText
429
            ) 
430
        await self._last_transition_id.write_value(
431
            self._suspended_to_ready.nodeid, 
432
            varianttype=ua.VariantType.NodeId
433
            )
434
        #FIXME 
435
        # trigger ProgramTransitionEventType and 
436
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
437
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
438
439
    #Transition 
440
    async def ReadyToHalted(self):
441
        await self._current_state.write_value(
442
            self.localizedtext_halted,
443
            varianttype=ua.VariantType.LocalizedText
444
            ) 
445
        await self._current_state_id.write_value(
446
            self._halted_state.nodeid, 
447
            varianttype=ua.VariantType.NodeId
448
            )
449
        await self._last_transition.write_value(
450
            self.localizedtext_ready_to_halted,
451
            varianttype=ua.VariantType.LocalizedText
452
            ) 
453
        await self._last_transition_id.write_value(
454
            self._ready_to_halted.nodeid, 
455
            varianttype=ua.VariantType.NodeId
456
            )
457
        #FIXME 
458
        # trigger ProgramTransitionEventType and 
459
        # AuditUpdateMethodEvents/AuditProgramTransitionEventType (https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.2/)
460
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
461
462
    #method to be linked to uamethod
463
    async def Start(self):
464
        if await self._current_state.read_value() == self.localizedtext_ready:
465
            return await ReadyToRunning()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ReadyToRunning does not seem to be defined.
Loading history...
466
        else:
467
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
468
469
    #method to be linked to uamethod
470
    async def Suspend(self):
471
        if await self._current_state.read_value() == self.localizedtext_running:
472
            return await RunningToSuspended()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable RunningToSuspended does not seem to be defined.
Loading history...
473
        else:
474
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
475
476
    #method to be linked to uamethod
477
    async def Resume(self):
478
        if await self._current_state.read_value() == self.localizedtext_suspended:
479
            return await SuspendedToRunning()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable SuspendedToRunning does not seem to be defined.
Loading history...
480
        else:
481
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
482
483
    #method to be linked to uamethod
484
    async def Halt(self):
485
        if await self._current_state.read_value() == self.localizedtext_ready:
486
            return await ReadyToHalted()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ReadyToHalted does not seem to be defined.
Loading history...
487
        elif await self._current_state.read_value() == self.localizedtext_running:
488
            return await RunningToHalted()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable RunningToHalted does not seem to be defined.
Loading history...
489
        elif await self._current_state.read_value() == self.localizedtext_suspended:
490
            return await SuspendedToHalted()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable SuspendedToHalted does not seem to be defined.
Loading history...
491
        else:
492
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
493
494
    #method to be linked to uamethod
495
    async def Reset(self):
496
        if await self._current_state.read_value() == self.localizedtext_halted:
497
            return await HaltedToReady()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable HaltedToReady does not seem to be defined.
Loading history...
498
        else:
499
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
500
501
class ShelvedStateMachineTypeClass(FiniteStateMachineTypeClass):
502
    '''
503
    NOT IMPLEMENTED "ShelvedStateMachineType"
504
    '''
505
    def __init__(self, server=None, parent=None, idx=None, name=None):
506
        super().__init__(server, parent, idx, name)
507
        if name == None:
508
            name = "ShelvedStateMachine"
509
        self._state_machine_type = ua.NodeId(2929, 0)
510
        raise NotImplementedError
511
512
513
514
#FIXME REMOVE BEFOR MERGE
515
#Devtest / Workbench
516
if __name__ == "__main__":
517
    async def main():
518
        logging.basicConfig(level=logging.INFO)
519
        _logger = logging.getLogger('asyncua')
520
521
        server = Server()
522
        await server.init()
523
524
        sm = StateMachineTypeClass(server, server.nodes.objects, 0, "StateMachine")
525
        await sm.install(True)
526
        await sm.change_state(ua.LocalizedText("Test", "en-US"), server.get_node("ns=0;i=2253").nodeid)
527
        fsm = FiniteStateMachineTypeClass(server, server.nodes.objects, 0, "FiniteStateMachine")
528
        await fsm.install(True)
529
        pfsm = ProgramStateMachineTypeClass(server, server.nodes.objects, 0, "ProgramStateMachine")
530
        await pfsm.install(True)
531
532
        async with server:
533
            while 1:
534
                await asyncio.sleep(0)
535
536
    asyncio.run(main())
537