-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathapp_sample.py
340 lines (294 loc) · 11.9 KB
/
app_sample.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# -*- coding: utf-8 -*-
"""
Copyright (c) 2019 Fraunhofer Institute for Manufacturing Engineering and Automation (IPA)
Authors: Daniel Stock, Matthias Stoehr
Licensed under the Apache License, Version 2.0
See the file "LICENSE" for the full license governing this code.
"""
import datetime
import threading
import uuid
from msb_client.ComplexDataFormat import ComplexDataFormat
from msb_client.DataType import DataType
from msb_client.Event import Event
from msb_client.Function import Function
from msb_client.MsbClient import MsbClient
if __name__ == "__main__":
"""This is a sample client for the MSB python client library."""
# define service properties as constructor parameters
SERVICE_TYPE = "SmartObject"
SO_UUID = str(uuid.uuid1())
SO_NAME = "MSBClientPythonAppSample" + SO_UUID[-6:]
SO_DESCRIPTION = "MSBClientPythonAppSample description"
SO_TOKEN = SO_UUID[-6:]
myMsbClient = MsbClient(
SERVICE_TYPE,
SO_UUID,
SO_NAME,
SO_DESCRIPTION,
SO_TOKEN,
)
# otherwise the application.properties file will be read
# myMsbClient = MsbClient()
# msb_url = 'wss://localhost:8084'
# msb_url = 'ws://localhost:8085'
msb_url = 'ws://ws2.msb.edu.virtualfortknox.de'
# enable debug log messages (default = disabled).
myMsbClient.enableDebug(True)
# enable ws lib trace console output (default = disabled).
myMsbClient.enableTrace(False)
# enable data format and message data validation (default = enabled).
# might impact performance
myMsbClient.enableDataFormatValidation(True)
# enable auto reconnect for the client (default = enabled).
myMsbClient.disableAutoReconnect(False)
# set the reconnect interval time in ms (default = 10000 ms).
myMsbClient.setReconnectInterval(10000)
# enable or disable the message buffer,
# which will buffer sent event messages when no active connection is available (default = enabled).
myMsbClient.disableEventCache(False)
# set event cache size (default = 1000 message events).
myMsbClient.setEventCacheSize(1000)
# disable SSL hostname check and certificate validation for self signed certificates (default = enabled).
myMsbClient.disableHostnameVerification(True)
# add a configuration parameter to the self description.
# configuration parameters are published to the MSB and can be changed from the MSB GUI in real time
# (key, value, datatype)
myMsbClient.addConfigParameter("testParam1", 17, DataType.INT32)
myMsbClient.addConfigParameter("testParam2", "Hello World!", DataType.STRING)
myMsbClient.addConfigParameter("testParam3", 3.3, DataType.FLOAT)
myMsbClient.addConfigParameter("testParam4", True, DataType.BOOLEAN)
# to retrieve a configuration parameter, you can retrieve it as follows:
# myMsbClient.getConfigParameter('testParam1')
# e.g.
def printParameter():
print(str(myMsbClient.getConfigParameter("testParam1")))
# change a configuration parameter locally:
# myMsbClient.changeConfigParameter('testParam1', 17)
# create new event object
# parameter 1 (str:‘EVENT1’): internal event name reference (inside program code)
# parameter 2 (str:‘Event1’): MSB event name (visible in MSB GUI)
# parameter 3 (str:'Event1_description’): description which shows up in MSB GUI
# parameter 4 (DataType:DataType.STRING): type of event payload
# parameter 5 (int:1): event priority – value range: [0, 1, 2] (low, medium, high)
# parameter 6 (bool:optional): True if payload is an array of parameter 4
event1 = Event("EVENT1", "Event1", "Event1_description", DataType.STRING, 1)
# optionally define the data format as an array
event2 = Event("EVENT2", "Event2", "Event2_description", DataType.INT32, 0, True)
# if the event doesn't have a payload, just pass None as the data type parameter
event3 = Event("EVENT3", "Event3", "Event3_description", None, 0)
# event to demonstrate reponse events
response_event1 = Event(
"RESPONSE_EVENT1",
"Response Event1",
"ResponseEvent1_description",
DataType.STRING,
1,
)
# the final data format can be provided as a valid JSON string, the array function parameter will be ignored.
manual_event = Event(
"MANUAL_EVENT",
"Manual event",
"Manual event description",
'{ "type": "number", "format": "double" }',
0,
True,
)
# the final data format can be provided as a valid JSON object
complex_json_event = Event(
"COMPLEX_JSON_EVENT",
"Manual event",
"Manual event description",
{
"Member": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"status": {
"enum": ["present", "absent"],
"type": "string"
}
}
},
"Team": {
"type": "object",
"properties": {
"staff": {
"type": "array",
"items": {
"$ref": "#/definitions/Member"
}
}
}
},
"dataObject": {
"$ref": "#/definitions/Team"
}
},
0,
False,
)
# add event objects to MSB client
myMsbClient.addEvent(event1)
myMsbClient.addEvent(event2)
myMsbClient.addEvent(event3)
myMsbClient.addEvent(response_event1)
myMsbClient.addEvent(manual_event)
myMsbClient.addEvent(complex_json_event)
# optionally, add an event directly in line
myMsbClient.addEvent(
"EVENT4", "Event4", "Event4_description", DataType.INT32, 0, False
)
myMsbClient.addEvent(
"DATEEVENT", "DateEvent", "DateEvent_description", DataType.DATETIME, 0, False
)
# define a complex data format to be used in an event
# init the complex data format
myDevice = ComplexDataFormat("MyDevice")
myModule = ComplexDataFormat("MyModule")
# add the properties to the complex objects
# (property_name, property_datatype, isArray)
myModule.addProperty("moduleName", DataType.STRING, False)
myDevice.addProperty("deviceName", DataType.STRING, False)
myDevice.addProperty("deviceWeight", DataType.FLOAT, False)
myDevice.addProperty("submodules", myModule, True)
# add the event with the complex data format
myMsbClient.addEvent(
"EVENT5", "Event5", "Event5_description", myDevice, 0, False
)
# define the function which will be passed to the function description
def printMsg(msg):
print(str(msg["dataObject"]))
# define the function which will be passed to the function description
# this example shows
# When a function call is calling a function which has a response event,
# you have to pass the "correlationId" parameter which is provided by the function callback
# to the client's publish function. If you pass "None", nothing will be attached.
def sendResponseEventExample(msg):
print(str(msg))
# don't forget to pass the parameter name for the correlationId if you're not passing all parameters
myMsbClient.publish(
"RESPONSE_EVENT1", msg["dataObject"], correlationId=msg["correlationId"]
)
# create new function object
# This example has no response events.
# parameter 1 (str:‘FUNCTION1’): internal function name reference (inside program code)
# parameter 2 (str:‘Function1’): MSB function name (visible in MSB GUI)
# parameter 3 (str:'Function1_description’): description which shows up in MSB GUI
# parameter 4 (DataType:DataType.STRING): type of data the function will handle
# parameter 5 (fnPointer:printMsg): pointer to the function implementation
# parameter 6 (bool:optional): True if payload is an array of parameter 4
# parameter 7 (list:optional): array of response events e.g. ['RESPONSE_EVENT1']
function1 = Function(
"FUNCTION1", "Function1", "Function1_description", DataType.STRING, printMsg, True, []
)
# add function objects to MSB client
myMsbClient.addFunction(function1)
# optionally, add function directly in line
# this example has one response event.
myMsbClient.addFunction(
"FUNCTION2",
"Function2",
"Function2_description",
DataType.STRING,
sendResponseEventExample,
False,
["RESPONSE_EVENT1"],
)
# define a function with a complex data format
f3 = Function(
"FUNCTION3",
"Function3",
"Function3_description",
myDevice,
printMsg,
False,
)
myMsbClient.addFunction(f3)
# if the function is not requiring any parameters None can be passed for the data format
# This example has multiple response events.
myMsbClient.addFunction(
"PRINT_PARAMETER",
"Print parameter",
"Print parameter",
None,
printParameter,
False,
["EVENT1", "EVENT2"],
)
# the final data format can be provided as a valid JSON object
myMsbClient.addFunction(
"COMPLEX_JSON_FUNCTION",
"Function JSON based",
"Description function JSON based",
{
"Member": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"status": {
"enum": ["present", "absent"],
"type": "string"
}
}
},
"Team": {
"type": "object",
"properties": {
"staff": {
"type": "array",
"items": {
"$ref": "#/definitions/Member"
}
}
}
},
"dataObject": {
"$ref": "#/definitions/Team"
}
},
printMsg,
False,
["EVENT1", "EVENT2"],
)
# helper function which will repeat the passed function in the defined interval in seconds,
# this example doesn't check for race condition
def set_interval(func, sec):
def func_wrapper():
set_interval(func, sec)
func()
t = threading.Timer(sec, func_wrapper)
t.start()
return t
def send_data():
# myMsbClient.publish("EVENT1", "Hello World!")
# myMsbClient.publish('EVENT2', [1,2,4,5])
# myMsbClient.publish('DATEEVENT', datetime.datetime.now())
# pepare the complex ovbject based on a complex data format
# use it as event value
myModuleObj = {}
myModuleObj['moduleName'] = 'Module 1'
myDeviceObj = {}
myDeviceObj['deviceName'] = 'Device 1'
myDeviceObj['deviceWeight'] = 1.3
myDeviceObj['submodules'] = [myModuleObj]
myMsbClient.publish('EVENT5', myDeviceObj)
# print the generated self description for debug purposes. This function has to be called after all events,
# functions and parameters have been added or else the output will be incomplete.
print(myMsbClient.objectToJson(myMsbClient.getSelfDescription()))
# connect by defining a broker url in line
# myMsbClient.connect('wss://localhost:8084')
# connect to server by defining server url in line, otherwise the application.properties file will be read
myMsbClient.connect(msb_url)
# myMsbClient.connect()
# register client on MSB
myMsbClient.register()
# disconnect client from MSB
# myMsbClient.disconnect()
# call send_data() function every 5 seconds
# be aware of racing conditions which might be created here
set_interval(send_data, 5)