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

asyncua.common.statemachine   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 466
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 314
dl 0
loc 466
rs 7.92
c 0
b 0
f 0
wmc 51

28 Methods

Rating   Name   Duplication   Size   Complexity  
A StateMachineTypeClass.__init__() 0 18 5
A FiniteStateMachineTypeClass.__init__() 0 7 2
A ProgramStateMachineTypeClass.install() 0 30 1
A StateMachineTypeClass.write_state() 0 4 1
A ProgramStateMachineTypeClass.Resume() 0 5 2
A FiniteStateMachineTypeClass.set_avalible_states() 0 2 1
A ShelvedStateMachineTypeClass.__init__() 0 6 2
A ProgramStateMachineTypeClass.SuspendedToRunning() 0 19 1
A ProgramStateMachineTypeClass.SuspendedToHalted() 0 19 1
A ProgramStateMachineTypeClass.ReadyToRunning() 0 19 1
A StateMachineTypeClass.change_state() 0 13 4
A ExclusiveLimitStateMachineTypeClass.__init__() 0 6 2
A ProgramStateMachineTypeClass.RunningToHalted() 0 19 1
A ProgramStateMachineTypeClass.__init__() 0 39 2
A ProgramStateMachineTypeClass.HaltedToReady() 0 19 1
A ProgramStateMachineTypeClass.SuspendedToReady() 0 19 1
A ProgramStateMachineTypeClass.Start() 0 5 2
A StateMachineTypeClass.install() 0 19 2
A ProgramStateMachineTypeClass.Suspend() 0 5 2
A ProgramStateMachineTypeClass.Reset() 0 5 2
A ProgramStateMachineTypeClass.ReadyToHalted() 0 19 1
A StateMachineTypeClass.write_transition() 0 4 1
A ProgramStateMachineTypeClass.RunningToReady() 0 19 1
A FiniteStateMachineTypeClass.set_avalible_transitions() 0 2 1
A ProgramStateMachineTypeClass.RunningToSuspended() 0 19 1
A ProgramStateMachineTypeClass.Halt() 0 9 4
A FileTransferStateMachineTypeClass.__init__() 0 6 2
A FiniteStateMachineTypeClass.install() 0 10 1

1 Function

Rating   Name   Duplication   Size   Complexity  
A main() 0 18 3

How to fix   Complexity   

Complexity

Complex classes like asyncua.common.statemachine 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
import asyncio, logging
2
3
from asyncua import Server, ua, Node
4
from asyncua.common.instantiate_util import instantiate
5
6
class StateMachineTypeClass(object):
7
    '''
8
    Implementation of an StateMachineType
9
    '''
10
    def __init__(self, server=None, parent=None, idx=None, name=None):
11
        if not server: raise ValueError #FIXME change to check instance type
12
        if not parent: raise ValueError #FIXME change to check instance type
13
        if idx == None:
14
            idx = parent.nodeid.NamespaceIndex
15
        if name == None:
16
            name = "StateMachine"
17
        self._server = server
18
        self._parent = parent
19
        self._state_machine_node = None
20
        self._state_machine_type = ua.NodeId(2299, 0)
21
        self._name = name
22
        self._idx = idx
23
        self._optionals = False
24
        self._current_state_node = None
25
        self._current_state_id_node = None
26
        self._last_transition_node = None
27
        self._last_transition_node_id = None
28
29
    async def install(self, optionals=False):
30
        '''
31
        setup adressspace and initialize 
32
        '''
33
        self._optionals = optionals
34
        self._state_machine_node = await self._parent.add_object(
35
            self._idx, 
36
            self._name, 
37
            objecttype=self._state_machine_type, 
38
            instantiate_optional=optionals
39
            )
40
        self._current_state_node = await self._state_machine_node.get_child(["CurrentState"])
41
        self._current_state_id_node = await self._state_machine_node.get_child(["CurrentState","Id"])
42
        if self._optionals:
43
            self._current_transition_node = await self._state_machine_node.get_child(["LastTransition"])
44
            self._current_transition_id_node = await self._state_machine_node.get_child(["LastTransition","Id"])
45
46
        #FIXME initialise values
47
        '''
48
        maybe its smart to check parents child for a initial state instance (InitialStateType) 
49
        and initialize it with its id but if no state instance is provided ... i will log a 
50
        warning
51
        '''
52
53
    async def change_state(self, state_name, state_node, transition_name=None, transition_node=None):
54
        '''
55
        method to change the state of the statemachine
56
        state_name: ua.LocalizedText()
57
        state: ua.NodeId() <- StateType node
58
        transition_name: ua.LocalizedText()
59
        transition: ua.NodeId() <- TransitionType node
60
        '''
61
        #FIXME check StateType exist
62
        #FIXME check TransitionTypeType exist
63
        await self.write_state(state_name, state_node)
64
        if self._optionals and transition_name and transition_node:
65
            await self.write_transition(transition_name, transition_node)
66
67
    async def write_state(self, state_name, state_node):
68
        #FIXME check types/class
69
        await self._current_state_node.write_value(state_name)
70
        await self._current_state_id_node.write_value(state_node)
71
72
    async def write_transition(self, transition_name, transition_node):
73
        #FIXME check types/class
74
        await self._last_transition_node.write_value(transition_name)
75
        await self._last_transition_id_node.write_value(transition_node)
76
77
class FiniteStateMachineTypeClass(StateMachineTypeClass):
78
    '''
79
    Implementation of an FiniteStateMachineType
80
    '''
81
    def __init__(self, server=None, parent=None, idx=None, name=None):
82
        super().__init__(server, parent, idx, name)
83
        if name == None:
84
            name = "FiniteStateMachine"
85
        self._state_machine_type = ua.NodeId(2771, 0)
86
        self._avalible_states = []
87
        self._avalible_transitions = []
88
89
    async def install(self, optionals=False):
90
        '''
91
        setup adressspace and initialize 
92
        '''
93
        self._optionals = optionals
94
        self._state_machine_node = await self._parent.add_object(
95
            self._idx, 
96
            self._name, 
97
            objecttype=self._state_machine_type, 
98
            instantiate_optional=optionals
99
            )
100
101
    async def set_avalible_states(self, states):
102
        self._avalible_states = states
103
104
    async def set_avalible_transitions(self, transitions):
105
        self._avalible_transitions = transitions      
106
107
class ExclusiveLimitStateMachineTypeClass(FiniteStateMachineTypeClass):
108
    '''
109
    NOT IMPLEMENTED "ExclusiveLimitStateMachineType"
110
    '''
111
    def __init__(self, server=None, parent=None, idx=None, name=None):
112
        super().__init__(server, parent)
113
        if name == None:
114
            name = "ExclusiveLimitStateMachine"
115
        self._state_machine_type = ua.NodeId(9318, 0)
116
        raise NotImplementedError
117
118
class FileTransferStateMachineTypeClass(FiniteStateMachineTypeClass):
119
    '''
120
    NOT IMPLEMENTED "FileTransferStateMachineType"
121
    '''
122
    def __init__(self, server=None, parent=None, idx=None, name=None):
123
        super().__init__(server, parent)
124
        if name == None:
125
            name = "FileTransferStateMachine"
126
        self._state_machine_type = ua.NodeId(15803, 0)
127
        raise NotImplementedError
128
129
class ProgramStateMachineTypeClass(FiniteStateMachineTypeClass):
130
    '''
131
    https://reference.opcfoundation.org/v104/Core/docs/Part10/4.2.3/
132
    '''
133
    def __init__(self, server=None, parent=None, idx=None, name=None):
134
        super().__init__(server, parent, idx, name)
135
        if name == None:
136
            name = "ProgramStateMachine"
137
        self._state_machine_type = ua.NodeId(2391, 0)
138
        self._ready_state = None #State node
139
        self._halted_state = None #State node
140
        self._running_state = None #State node
141
        self._suspended_state = None #State node
142
143
        self._halted_to_ready = None #Transition node
144
        self._ready_to_running = None #Transition node
145
        self._running_to_halted = None #Transition node
146
        self._running_to_ready = None #Transition node
147
        self._running_to_suspended = None #Transition node
148
        self._suspended_to_running = None #Transition node
149
        self._suspended_to_halted = None #Transition node
150
        self._suspended_to_ready = None #Transition node
151
        self._ready_to_halted = None #Transition node
152
153
        self._halt_method_node = None #uamethod node
154
        self._reset_method_node = None #uamethod node
155
        self._resume_method_node = None #uamethod node
156
        self._start_method_node = None #uamethod node
157
        self._suspend_method_node = None #uamethod node
158
159
        self.lt_ready = ua.LocalizedText("Ready", "en-US")
160
        self.lt_running = ua.LocalizedText("Running", "en-US")
161
        self.lt_halted = ua.LocalizedText("Halted", "en-US")
162
        self.lt_suspended= ua.LocalizedText("Suspended", "en-US")
163
        self.lt_halted_to_ready = ua.LocalizedText("HaltedToReady", "en-US")
164
        self.lt_ready_to_running = ua.LocalizedText("ReadyToRunning", "en-US")
165
        self.lt_running_to_halted = ua.LocalizedText("RunningToHalted", "en-US")
166
        self.lt_running_to_ready = ua.LocalizedText("RunningToReady", "en-US")
167
        self.lt_running_to_suspended = ua.LocalizedText("RunningToSuspended", "en-US")
168
        self.lt_suspended_to_running = ua.LocalizedText("SuspendedToRunning", "en-US")
169
        self.lt_suspended_to_halted = ua.LocalizedText("SuspendedToHalted", "en-US")
170
        self.lt_suspended_to_ready = ua.LocalizedText("SuspendedToReady", "en-US")
171
        self.lt_ready_to_halted = ua.LocalizedText("ReadyToHalted", "en-US")
172
173
    async def install(self, optionals=False):
174
        '''
175
        setup adressspace and initialize 
176
        '''
177
        self._optionals = optionals
178
        self._state_machine_node = await self._parent.add_object(
179
            self._idx, 
180
            self._name, 
181
            objecttype=self._state_machine_type, 
182
            instantiate_optional=optionals
183
            )
184
        #FIXME get childnodes:
185
        self._ready_state = None #State node
186
        self._halted_state = None #State node
187
        self._running_state = None #State node
188
        self._suspended_state = None #State node
189
        self._halted_to_ready = None #Transition node
190
        self._ready_to_running = None #Transition node
191
        self._running_to_halted = None #Transition node
192
        self._running_to_ready = None #Transition node
193
        self._running_to_suspended = None #Transition node
194
        self._suspended_to_running = None #Transition node
195
        self._suspended_to_halted = None #Transition node
196
        self._suspended_to_ready = None #Transition node
197
        self._ready_to_halted = None #Transition node
198
        self._halt_method_node = None #uamethod node
199
        self._reset_method_node = None #uamethod node
200
        self._resume_method_node = None #uamethod node
201
        self._start_method_node = None #uamethod node
202
        self._suspend_method_node = None #uamethod node
203
204
    #Transition
205
    async def HaltedToReady(self):
206
        await self._current_state.write_value(
207
            self.lt_ready,
208
            varianttype=ua.VariantType.LocalizedText
209
            ) 
210
        await self._current_state_id.write_value(
211
            self._ready_state.nodeid, 
212
            varianttype=ua.VariantType.NodeId
213
            )
214
        await self._last_transition.write_value(
215
            self.lt_halted_to_ready,
216
            varianttype=ua.VariantType.LocalizedText
217
            ) 
218
        await self._last_transition_id.write_value(
219
            self._halted_to_ready.nodeid,
220
            varianttype=ua.VariantType.NodeId
221
            )
222
        #FIXME trigger event
223
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
224
225
    #Transition
226
    async def ReadyToRunning(self):
227
        await self._current_state.write_value(
228
            self.lt_running,
229
            varianttype=ua.VariantType.LocalizedText
230
            ) 
231
        await self._current_state_id.write_value(
232
            self._running_state.nodeid, 
233
            varianttype=ua.VariantType.NodeId
234
            )
235
        await self._last_transition.write_value(
236
            self.lt_ready_to_running,
237
            varianttype=ua.VariantType.LocalizedText
238
            ) 
239
        await self._last_transition_id.write_value(
240
            self._ready_to_running.nodeid, 
241
            varianttype=ua.VariantType.NodeId
242
            )
243
        #FIXME trigger event
244
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
245
246
    #Transition
247
    async def RunningToHalted(self):
248
        await self._current_state.write_value(
249
            self.lt_halted,
250
            varianttype=ua.VariantType.LocalizedText
251
            ) 
252
        await self._current_state_id.write_value(
253
            self._halted_state.nodeid, 
254
            varianttype=ua.VariantType.NodeId
255
            )
256
        await self._last_transition.write_value(
257
            self.lt_running_to_halted,
258
            varianttype=ua.VariantType.LocalizedText
259
            ) 
260
        await self._last_transition_id.write_value(
261
            self._running_to_halted.nodeid, 
262
            varianttype=ua.VariantType.NodeId
263
            )
264
        #FIXME trigger event
265
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
266
267
    #Transition
268
    async def RunningToReady(self):
269
        await self._current_state.write_value(
270
            self.lt_ready,
271
            varianttype=ua.VariantType.LocalizedText
272
            ) 
273
        await self._current_state_id.write_value(
274
            self._ready_state.nodeid, 
275
            varianttype=ua.VariantType.NodeId
276
            )
277
        await self._last_transition.write_value(
278
            self.lt_running_to_ready,
279
            varianttype=ua.VariantType.LocalizedText
280
            ) 
281
        await self._last_transition_id.write_value(
282
            self._running_to_ready.nodeid, 
283
            varianttype=ua.VariantType.NodeId
284
            )
285
        #FIXME trigger event
286
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
287
288
    #Transition
289
    async def RunningToSuspended(self):
290
        await self._current_state.write_value(
291
            self.lt_suspended,
292
            varianttype=ua.VariantType.LocalizedText
293
            ) 
294
        await self._current_state_id.write_value(
295
            self._suspended_state.nodeid, 
296
            varianttype=ua.VariantType.NodeId
297
            )
298
        await self._last_transition.write_value(
299
            self.lt_running_to_suspended,
300
            varianttype=ua.VariantType.LocalizedText
301
            ) 
302
        await self._last_transition_id.write_value(
303
            self._running_to_suspended.nodeid, 
304
            varianttype=ua.VariantType.NodeId
305
            )
306
        #FIXME trigger event
307
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
308
309
    #Transition 
310
    async def SuspendedToRunning(self):
311
        await self._current_state.write_value(
312
            self.lt_running,
313
            varianttype=ua.VariantType.LocalizedText
314
            ) 
315
        await self._current_state_id.write_value(
316
            self._running_state.nodeid, 
317
            varianttype=ua.VariantType.NodeId
318
            )
319
        await self._last_transition.write_value(
320
            self.lt_suspended_to_running,
321
            varianttype=ua.VariantType.LocalizedText
322
            )
323
        await self._last_transition_id.write_value(
324
            self._suspended_to_running.nodeid, 
325
            varianttype=ua.VariantType.NodeId
326
            )
327
        #FIXME trigger event
328
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
329
330
    #Transition
331
    async def SuspendedToHalted(self):
332
        await self._current_state.write_value(
333
            self.lt_halted,
334
            varianttype=ua.VariantType.LocalizedText
335
            ) 
336
        await self._current_state_id.write_value(
337
            self._halted_state.nodeid, 
338
            varianttype=ua.VariantType.NodeId
339
            )
340
        await self._last_transition.write_value(
341
            self.lt_suspended_to_halted,
342
            varianttype=ua.VariantType.LocalizedText
343
            ) 
344
        await self._last_transition_id.write_value(
345
            self._suspended_to_halted.nodeid, 
346
            varianttype=ua.VariantType.NodeId
347
            )
348
        #FIXME trigger event
349
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
350
351
    #Transition
352
    async def SuspendedToReady(self):
353
        await self._current_state.write_value(
354
            self.lt_ready,
355
            varianttype=ua.VariantType.LocalizedText
356
            ) 
357
        await self._current_state_id.write_value(
358
            self._ready_state.nodeid, 
359
            varianttype=ua.VariantType.NodeId
360
            )
361
        await self._last_transition.write_value(
362
            self.lt_suspended_to_ready,
363
            varianttype=ua.VariantType.LocalizedText
364
            ) 
365
        await self._last_transition_id.write_value(
366
            self._suspended_to_ready.nodeid, 
367
            varianttype=ua.VariantType.NodeId
368
            )
369
        #FIXME trigger event
370
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
371
372
    #Transition 
373
    async def ReadyToHalted(self):
374
        await self._current_state.write_value(
375
            self.lt_halted,
376
            varianttype=ua.VariantType.LocalizedText
377
            ) 
378
        await self._current_state_id.write_value(
379
            self._halted_state.nodeid, 
380
            varianttype=ua.VariantType.NodeId
381
            )
382
        await self._last_transition.write_value(
383
            self.lt_ready_to_halted,
384
            varianttype=ua.VariantType.LocalizedText
385
            ) 
386
        await self._last_transition_id.write_value(
387
            self._ready_to_halted.nodeid, 
388
            varianttype=ua.VariantType.NodeId
389
            )
390
        #FIXME trigger event
391
        return ua.StatusCode(ua.status_codes.StatusCodes.Good)
392
393
    #method to be linked to uamethod
394
    async def Start(self):
395
        if await self._current_state.read_value() == self.lt_ready:
396
            return await ReadyToRunning()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ReadyToRunning does not seem to be defined.
Loading history...
397
        else:
398
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
399
400
    #method to be linked to uamethod
401
    async def Suspend(self):
402
        if await self._current_state.read_value() == self.lt_running:
403
            return await RunningToSuspended()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable RunningToSuspended does not seem to be defined.
Loading history...
404
        else:
405
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
406
407
    #method to be linked to uamethod
408
    async def Resume(self):
409
        if await self._current_state.read_value() == self.lt_suspended:
410
            return await SuspendedToRunning()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable SuspendedToRunning does not seem to be defined.
Loading history...
411
        else:
412
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
413
414
    #method to be linked to uamethod
415
    async def Halt(self):
416
        if await self._current_state.read_value() == self.lt_ready:
417
            return await ReadyToHalted()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ReadyToHalted does not seem to be defined.
Loading history...
418
        elif await self._current_state.read_value() == self.lt_running:
419
            return await RunningToHalted()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable RunningToHalted does not seem to be defined.
Loading history...
420
        elif await self._current_state.read_value() == self.lt_suspended:
421
            return await SuspendedToHalted()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable SuspendedToHalted does not seem to be defined.
Loading history...
422
        else:
423
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
424
425
    #method to be linked to uamethod
426
    async def Reset(self):
427
        if await self._current_state.read_value() == self.lt_halted:
428
            return await HaltedToReady()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable HaltedToReady does not seem to be defined.
Loading history...
429
        else:
430
            return ua.StatusCode(ua.status_codes.StatusCodes.BadNotExecutable)
431
432
class ShelvedStateMachineTypeClass(FiniteStateMachineTypeClass):
433
    '''
434
    NOT IMPLEMENTED "ShelvedStateMachineType"
435
    '''
436
    def __init__(self, server=None, parent=None, idx=None, name=None):
437
        super().__init__(server, parent)
438
        if name == None:
439
            name = "ShelvedStateMachine"
440
        self._state_machine_type = ua.NodeId(2929, 0)
441
        raise NotImplementedError
442
443
444
#Devtests
445
async def main():
446
    logging.basicConfig(level=logging.INFO)
447
    _logger = logging.getLogger('asyncua')
448
449
    server = Server()
450
    await server.init()
451
452
    sm = StateMachineTypeClass(server, server.nodes.objects, 0, "StateMachine")
453
    await sm.install(True)
454
    await sm.change_state(ua.LocalizedText("Test", "en-US"), server.get_node("ns=0;i=2253").nodeid)
455
    fsm = FiniteStateMachineTypeClass(server, server.nodes.objects, 0, "FiniteStateMachine")
456
    await fsm.install(True)
457
    pfsm = ProgramStateMachineTypeClass(server, server.nodes.objects, 0, "ProgramStateMachine")
458
    await pfsm.install(True)
459
460
    async with server:
461
        while 1:
462
            await asyncio.sleep(0)
463
464
if __name__ == "__main__":
465
    asyncio.run(main())
466