p3lib 1.1.108__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
p3lib/helper.py ADDED
@@ -0,0 +1,420 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """This file is responsible for providing general helper functionality not
4
+ associated with particular objects"""
5
+
6
+ import sys
7
+ import os
8
+ import platform
9
+ import json
10
+ import traceback
11
+ import socket
12
+
13
+ def initArgs(parser, lastCmdLineArg=None, checkHostArg=True):
14
+ """This method is responsible for
15
+ - Ensure that the host argument has been defined by the user on the command line
16
+ - Set the debug level as defined on the command line.
17
+ - If lastCmdLineArg is defined then ensure that this is the last arguemnt on the
18
+ command line. If we don't do this then the user may define some arguments on
19
+ the command line after the action (callback) command line option but as
20
+ the arguments are processed in sequence the args following the action
21
+ arg will not be used.
22
+ """
23
+
24
+ if checkHostArg:
25
+ if parser.values.host == None or len(parser.values.host) == 0:
26
+ raise Exception("Please define the RFeye host on the command line.")
27
+
28
+ parser.uio.debugLevel = parser.values.debug
29
+
30
+ # Check that the argument that invoked tha action (callback) is the last on the command line
31
+ argOk = False
32
+ if len(sys.argv) > 0:
33
+ lastArg = sys.argv[len(sys.argv) - 1]
34
+ if lastArg == lastCmdLineArg:
35
+ argOk = True
36
+
37
+ if not argOk:
38
+ raise Exception("Please ensure %s (if used) is the last argument on the command line." % (lastCmdLineArg))
39
+
40
+
41
+ def ensureBoolInt(value, arg):
42
+ """We expect value to be a boolean (0 or 1)
43
+ raise an error if not
44
+ """
45
+ if value not in [0, 1]:
46
+ raise Exception("The %s arg should be followed by 0 or 1. Was %s." % (str(arg), str(value)))
47
+
48
+
49
+ def getLines(text):
50
+ """Split the text into lines"""
51
+ lines = []
52
+ if len(text) > 0:
53
+ elems = text.split("\n")
54
+ lines = stripEOL(elems)
55
+ return lines
56
+
57
+
58
+ def stripEOL(lines):
59
+ """Strip the end of line characters from the list of lines of text"""
60
+ noEOLLines = []
61
+ for l in lines:
62
+ l = l.rstrip("\n")
63
+ l = l.rstrip("\r")
64
+ noEOLLines.append(l)
65
+ return noEOLLines
66
+
67
+
68
+ def getLinesFromFile(f):
69
+ """Get Lines from file"""
70
+ fd = open(f, "r")
71
+ lines = fd.readlines()
72
+ fd.close()
73
+ return stripEOL(lines)
74
+
75
+
76
+ def _removeInvalidChars(line):
77
+ """Return a copy of line with each ASCII control character (0-31),
78
+ and each double quote, removed."""
79
+ output = ''
80
+ for c in line:
81
+ if c >= ' ' and c != '"':
82
+ output = output + c
83
+ return output
84
+
85
+
86
+ def _addEntry(line, dict):
87
+ """Parse line into a key and value, adding the result to dict, as in
88
+ getDict."""
89
+ # check for a parameter
90
+ fields = line.split('=')
91
+ # if at least 2 fields exist
92
+ if len(fields) > 1:
93
+ # add the key,value pair to the dictionary
94
+ key = _removeInvalidChars(fields[0])
95
+ value = _removeInvalidChars(fields[1])
96
+ dict[key] = value
97
+
98
+
99
+ def getDict(filename, jsonFmt=False):
100
+ """@brief Load dict from file
101
+ @param jsonFmt If True then we expect the file to be in json format.
102
+
103
+ if json is True we expect the file to be in json format
104
+
105
+ if json is False
106
+ We key=value pairs (= is the separate character).
107
+ Lines containing a hash sign as the first non-whitespace character are
108
+ ignored. Leading and trailing whitespace is ignored.
109
+
110
+ Lines not containing an equals sign are also silently ignored.
111
+
112
+ Lines not ignored are assumed to be in the form key=value, where key
113
+ does not contain an equals sign;
114
+ Control characters and double quotes in both key and value are silently
115
+ discarded. value is also truncated just before the first whitespace or
116
+ equals sign it contains.
117
+
118
+ @return Return the dict loaded from the file.
119
+
120
+ """
121
+ dictLoaded = {}
122
+
123
+ if jsonFmt:
124
+ fp = open(filename, 'r')
125
+ dictLoaded = json.load(fp)
126
+ fp.close()
127
+
128
+ else:
129
+
130
+ lines = getLinesFromFile(filename)
131
+ for line in lines:
132
+ # strip leading and trailing whitespaces
133
+ line = line.strip()
134
+ # if a comment line then ignore
135
+ if line.find('#') == 0:
136
+ continue
137
+ # add an entry to the dict
138
+ _addEntry(line, dictLoaded)
139
+
140
+ return dictLoaded
141
+
142
+
143
+ def saveDict(dictToSave, filename, jsonFmt=False):
144
+ """@brief Save dict to a file.
145
+ @param jsonFmt If True then we expect the file to be in json format.
146
+
147
+ if json is True we expect the file to be in json format
148
+
149
+ if json is False the file is saved as key = value pairs
150
+ Each key in dict produces a line of the form key=value. Output will be
151
+ ambiguous if any keys contain equals signs or if any values contain
152
+ newlines.
153
+
154
+ """
155
+
156
+ if jsonFmt:
157
+ try:
158
+
159
+ with open(filename, "w") as write_file:
160
+ json.dump(dictToSave, write_file)
161
+
162
+ except IOError as i:
163
+ raise IOError(i.errno, 'Failed to write file \'%s\': %s'
164
+ % (filename, i.strerror), i.filename).with_traceback(sys.exc_info()[2])
165
+ else:
166
+ lines = []
167
+ # build config file lines
168
+ for key in list(dictToSave.keys()):
169
+ lines.append(str(key) + '=' + str(dictToSave.get(key)) + '\n')
170
+ try:
171
+ f = open(filename, 'w')
172
+ f.writelines(lines)
173
+ f.close()
174
+ except IOError as i:
175
+ raise IOError(i.errno, 'Failed to write file \'%s\': %s'
176
+ % (filename, i.strerror), i.filename).with_traceback(sys.exc_info()[2])
177
+
178
+
179
+ def getAddrPort(host):
180
+ """The host address may be entered in the format <address>:<port>
181
+ Return a tuple with host and port"""
182
+ elems = host.split(":")
183
+
184
+ if len(elems) > 1:
185
+ host = elems[0]
186
+ port = int(elems[1])
187
+ else:
188
+ port = 22
189
+
190
+ return [host, port]
191
+
192
+
193
+ def getProgramName():
194
+ """Get the name of the currently running program."""
195
+ progName = sys.argv[0].strip()
196
+ if progName.startswith('./'):
197
+ progName = progName[2:]
198
+ if progName.endswith('.py'):
199
+ progName = progName[:-3]
200
+
201
+ # Only return the name of the program not the path
202
+ pName = os.path.split(progName)[-1]
203
+ if pName.endswith('.exe'):
204
+ pName = pName[:-4]
205
+ return pName
206
+
207
+
208
+ def getBoolUserResponse(uio, prompt, allowQuit=True):
209
+ """Get boolean (Y/N) repsonse from user.
210
+ If allowQuit is True and the user enters q then the program will exit."""
211
+ while True:
212
+ response = uio.getInput(prompt=prompt)
213
+ if response.lower() == 'y':
214
+ return True
215
+ elif response.lower() == 'n':
216
+ return False
217
+ elif allowQuit and response.lower() == 'q':
218
+ sys.exit(0)
219
+
220
+
221
+ def getIntUserResponse(uio, prompt, allowQuit=True):
222
+ """Get int repsonse from user.
223
+ If allowQuit is True and the user enters q then None is returned to
224
+ indicate that the user selected quit."""
225
+ while True:
226
+ response = uio.getInput(prompt=prompt)
227
+
228
+ try:
229
+
230
+ return int(response)
231
+
232
+ except ValueError:
233
+
234
+ uio.info("%s is not a valid integer value." % (response))
235
+
236
+ if allowQuit and response.lower() == 'q':
237
+ return None
238
+
239
+
240
+ def getIntListUserResponse(uio, prompt, minValue=None, maxValue=None, allowQuit=True):
241
+ """Get int repsonse from user as a list of int's
242
+ If allowQuit is True and the user enters q then the program will exit."""
243
+ while True:
244
+ response = uio.getInput(prompt=prompt)
245
+ try:
246
+ elems = response.split(",")
247
+ if len(elems) > 0:
248
+ intList = []
249
+ errorStr = None
250
+ for vStr in elems:
251
+ v = int(vStr)
252
+ if minValue != None and v < minValue:
253
+ errorStr = "The min value that may be entered is %d." % (minValue)
254
+ break
255
+ elif maxValue != None and v > maxValue:
256
+ errorStr = "The max value that may be entered is %d." % (maxValue)
257
+ break
258
+ else:
259
+ intList.append(v)
260
+
261
+ if errorStr != None:
262
+ uio.error(errorStr)
263
+
264
+ return intList
265
+ except ValueError:
266
+ pass
267
+ if allowQuit and response.lower() == 'q':
268
+ sys.exit(0)
269
+
270
+
271
+ def getHomePath():
272
+ """Get the user home path as this will be used to store config files"""
273
+ if platform.system() == 'Linux' and os.geteuid() == 0:
274
+ # Fix for os.environ["HOME"] returning /home/root sometimes.
275
+ return '/root/'
276
+
277
+ elif "HOME" in os.environ:
278
+ return os.environ["HOME"]
279
+
280
+ elif "HOMEDRIVE" in os.environ and "HOMEPATH" in os.environ:
281
+ return os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"]
282
+
283
+ elif "USERPROFILE" in os.environ:
284
+ return os.environ["USERPROFILE"]
285
+
286
+ return None
287
+
288
+
289
+ def setHomePath(homePath):
290
+ """Seth the env variable HOME"""
291
+ # Do some sanity/defensive stuff
292
+ if homePath == None:
293
+ raise Exception("homePath=None.")
294
+ elif len(homePath) == 0:
295
+ raise Exception("len(homePath)=0.")
296
+
297
+ # We do some special stuff on windows
298
+ if platform.system() == "Windows":
299
+
300
+ os.environ["HOME"] = homePath
301
+
302
+ if "USERPROFILE" in os.environ:
303
+ os.environ["USERPROFILE"] = os.environ["HOME"]
304
+
305
+ if "HOMEPATH" in os.environ:
306
+ os.environ["HOMEPATH"] = os.environ["HOME"]
307
+
308
+ if not os.path.isdir(os.environ["HOME"]):
309
+ raise Exception(os.environ["HOME"] + " path not found.")
310
+
311
+ else:
312
+ # Not windows set HOME env var
313
+ os.environ["HOME"] = homePath
314
+
315
+ if not os.path.isdir(os.environ["HOME"]):
316
+ raise Exception(os.environ["HOME"] + " path not found.")
317
+
318
+ def printDict(uio, theDict, indent=0):
319
+ """@brief Show the details of a dictionary contents
320
+ @param theDict The dictionary
321
+ @param indent Number of tab indents
322
+ @return None"""
323
+ for key in theDict:
324
+ uio.info('\t' * indent + str(key))
325
+ value = theDict[key]
326
+ if isinstance(value, dict):
327
+ printDict(uio, value, indent + 1)
328
+ else:
329
+ uio.info('\t' * (indent + 1) + str(value))
330
+
331
+ def logTraceBack(uio):
332
+ """@brief Log a traceback using the uio instance to the debug file.
333
+ @param uio A UIO instance
334
+ @return None"""
335
+ # Always store the exception traceback in the logfile as this makes
336
+ # it easier to diagnose problems with the testing
337
+ lines = traceback.format_exc().split("\n")
338
+ for line in lines:
339
+ uio.storeToDebugLog(line)
340
+
341
+ def GetFreeTCPPort():
342
+ """@brief Get a free port and return to the client. If no port is available
343
+ then -1 is returned.
344
+ @return the free TCP port number or -1 if no port is available."""
345
+ tcpPort=-1
346
+ try:
347
+ #Bind to a local port to find a free TTCP port
348
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
349
+ sock.bind(('', 0))
350
+ tcpPort = sock.getsockname()[1]
351
+ sock.close()
352
+ except socket.error:
353
+ pass
354
+ return tcpPort
355
+
356
+ def appendCreateFile(uio, aFile, quiet=False):
357
+ """@brief USer interaction to append or create a file.
358
+ @param uio A UIO instance.
359
+ @param quiet If True do not show uio messages (apart from overwrite prompt.
360
+ @param aFile The file to append or delete."""
361
+ createFile = False
362
+ if os.path.isfile(aFile):
363
+ if uio.getBoolInput("Overwrite {} y/n".format(aFile)):
364
+ os.remove(aFile)
365
+ if not quiet:
366
+ uio.info("Deleted {}".format(aFile))
367
+ createFile = True
368
+ else:
369
+ if not quiet:
370
+ uio.info("Appending to {}".format(aFile))
371
+
372
+ else:
373
+ createFile = True
374
+
375
+ if createFile:
376
+ fd = open(aFile, 'w')
377
+ fd.close()
378
+ if not quiet:
379
+ uio.info("Created {}".format(aFile))
380
+
381
+ def getAbsFile(filename):
382
+ """@brief Check that the file exists in several places.
383
+ 1 - The startup folder
384
+ 2 - An 'assets' folder in the startup folder
385
+ 3 - An 'assets' folder in the startup parent folder
386
+ 3 - In an 'assets' folder in a python site-packages folder.
387
+ @param filename The name of the icon file.
388
+ @return The abs path of the file or None if not found."""
389
+ file_found = None
390
+ abs_filename = os.path.abspath(filename)
391
+ if os.path.isfile(abs_filename):
392
+ file_found = abs_filename
393
+
394
+ else:
395
+ startup_file = os.path.abspath(sys.argv[0])
396
+ startup_path = os.path.dirname(startup_file)
397
+ path1 = os.path.join(startup_path, 'assets')
398
+ abs_filename = os.path.join(path1, filename)
399
+ if os.path.isfile(abs_filename):
400
+ file_found = abs_filename
401
+
402
+ else:
403
+ startup_parent_path = os.path.join(startup_path, '..')
404
+ path2 = os.path.join(startup_parent_path, 'assets')
405
+ abs_filename = os.path.join(path2, filename)
406
+ if os.path.isfile(abs_filename):
407
+ file_found = abs_filename
408
+
409
+ else:
410
+ # Try all the site packages folders we know about.
411
+ for path in sys.path:
412
+ if 'site-packages' in path:
413
+ site_packages_path = path
414
+ path3 = os.path.join(site_packages_path, 'assets')
415
+ abs_filename = os.path.join(path3, filename)
416
+ if os.path.isfile(abs_filename):
417
+ file_found = abs_filename
418
+ break
419
+
420
+ return file_found
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import socketserver
4
+ import socket
5
+ from struct import pack, unpack
6
+ import json
7
+ from time import sleep
8
+
9
+ class JSONNetworking(Exception):
10
+ pass
11
+
12
+ class JSONServer(socketserver.ThreadingTCPServer):
13
+ """@brief Responsible for accepting tcp connections to receive and send json messages."""
14
+
15
+ daemon_threads = True
16
+ allow_reuse_address = True
17
+
18
+ class JsonServerHandler (socketserver.BaseRequestHandler):
19
+
20
+ LEN_FIELD = 4
21
+ DEFAULT_RX_POLL_SECS = 0.02
22
+ DEFAULT_RX_BUFFER_SIZE = 2048
23
+
24
+ @staticmethod
25
+ def DictToJSON(aDict):
26
+ """@brief convert a python dictionary into JSON text
27
+ @param aDict The python dictionary to be converted
28
+ @return The JSON text representing aDict"""
29
+ return json.dumps(aDict)
30
+
31
+ @staticmethod
32
+ def JSONToDict(jsonText):
33
+ """@brief Convert from JSON text to a python dict. Throws a ValueError
34
+ if the text is formatted incorrectly.
35
+ @param jsonText The JSON text to be converted to a dict
36
+ @return a python dict object"""
37
+ return json.loads(jsonText)
38
+
39
+ @staticmethod
40
+ def TX(request, theDict):
41
+ """@brief Write the dict to the socket as json text
42
+ @param request The request object passed from the handle() method.
43
+ @theDict The python dictionary to send."""
44
+ if request:
45
+ msg = JsonServerHandler.DictToJSON(theDict).encode()
46
+ sFmt = ">%ds" % len(msg)
47
+ body = pack(sFmt, msg)
48
+ bodyLen = pack('>I', len(body))
49
+ txBuf = bodyLen + body
50
+ request.send(txBuf)
51
+
52
+ else:
53
+ raise RuntimeError("TX socket error")
54
+
55
+ @staticmethod
56
+ def GetBodyLen(rxBytes):
57
+ """@brief Get the length of the body of the message.
58
+ @param rxBytes The rx buffer containing bytes received.
59
+ @return The length of the body of the message or 0 if we do not have a complete message in the rx buffer."""
60
+ bodyLenFound = 0
61
+ #If we have enough data to extract the length field (start of PDU)
62
+ if len(rxBytes) >= JsonServerHandler.LEN_FIELD:
63
+ # Read the length of the message
64
+ bodyLen = unpack(">I", rxBytes[:JsonServerHandler.LEN_FIELD])[0]
65
+ #If we have the len field + the message body
66
+ if len(rxBytes) >= JsonServerHandler.LEN_FIELD+bodyLen:
67
+ bodyLenFound = bodyLen
68
+ return bodyLenFound
69
+
70
+ @staticmethod
71
+ def MsgAvail(rxBytes):
72
+ """@brief Determine is a complete message is present in the rx buffer.
73
+ @param rxBytes The rx buffer containing bytes received.
74
+ @return True if a complete message is present in the RX buffer."""
75
+ msgAvail = False
76
+
77
+ bodyLen = JsonServerHandler.GetBodyLen(rxBytes)
78
+
79
+ if bodyLen:
80
+ msgAvail = True
81
+
82
+ return msgAvail
83
+
84
+ def tx(self, request, theDict, throwError=True):
85
+ """@brief send a python dictionary object to the client via json.
86
+ @param request The request object to send data on.
87
+ @param theDict The dictionary to send
88
+ @param throwError If True then an exception will be thrown if an error occurs.
89
+ If False then this method will fail silentley.
90
+ @return True on success. False on failure if throwError = False"""
91
+ try:
92
+ JsonServerHandler.TX(request, theDict)
93
+ return True
94
+ except:
95
+ if throwError:
96
+ raise
97
+ return False
98
+
99
+ def _getDict(self):
100
+ """@brief Get dict from rx data.
101
+ @return The oldest dict in the rx buffer."""
102
+ rxDict = None
103
+ bodyLen = JsonServerHandler.GetBodyLen(self._rxBuffer)
104
+ #If we have a complete message in the RX buffer
105
+ if bodyLen > 0:
106
+ body = self._rxBuffer[JsonServerHandler.LEN_FIELD:JsonServerHandler.LEN_FIELD+bodyLen]
107
+ rxDict = JsonServerHandler.JSONToDict( body.decode() )
108
+ #Remove the message just received from the RX buffer
109
+ self._rxBuffer = self._rxBuffer[JsonServerHandler.LEN_FIELD+bodyLen:]
110
+
111
+ return rxDict
112
+
113
+ def rx(self, blocking=True,
114
+ pollPeriodSeconds=DEFAULT_RX_POLL_SECS,
115
+ rxBufferSize=DEFAULT_RX_BUFFER_SIZE):
116
+ """@brief Get a python dictionary object from the server.
117
+ @param blocking If True block until complete message is received.
118
+ @param pollPeriodSeconds If blocking wait for this period in seconds between checking for RX data.
119
+ @param rxBufferSize The size of the receive buffer in bytes.
120
+ @return A received dictionary of None if not blocking and no dictionary is available."""
121
+ #If we don't have an rx buffer, create one.
122
+ if '_rxBuffer' not in dir(self):
123
+ self._rxBuffer = bytearray()
124
+
125
+ while not JsonServerHandler.MsgAvail(self._rxBuffer):
126
+
127
+ try:
128
+ rxd = self.request.recv(rxBufferSize)
129
+ if len(rxd) > 0:
130
+ self._rxBuffer = self._rxBuffer + rxd
131
+ else:
132
+ raise RuntimeError("Socket closed")
133
+
134
+ if not blocking:
135
+ break
136
+
137
+ except BlockingIOError:
138
+ if not blocking:
139
+ break
140
+
141
+ if blocking:
142
+ sleep(pollPeriodSeconds)
143
+
144
+ return self._getDict()
145
+
146
+ def handle(self):
147
+ """@brief Handle connections to the server."""
148
+ raise JSONNetworking("!!! You must override this method in a subclass !!!")
149
+
150
+ class JSONClient(object):
151
+
152
+ def __init__(self, address, port, keepAliveActSec=1, keepAliveTxSec=15, keepAliveFailTriggerCount=8):
153
+ """@brief Connect to a JSONServer socket.
154
+ If on a Linux system then the connection will apply a keepalive to the TCP connection.
155
+ By default this is 2 minutes.
156
+ @param address The address of the JSONServer.
157
+ @param The port on the JSON server to connect to.
158
+ @param keepAliveActSec Activate the keepalive failure this many seconds after it is triggered.
159
+ @param keepAliveTxSec Send a TCP keepalive periodically. This defines the period in seconds.
160
+ @param keepAliveFailTriggerCount Trigger a keepalive failure when this many keepalives fail consecutively."""
161
+
162
+ self._socket = socket.socket()
163
+ self._socket.connect( (address, port) )
164
+
165
+ if hasattr(socket, 'SOL_SOCKET') and\
166
+ hasattr(socket, 'SO_KEEPALIVE') and\
167
+ hasattr(socket, 'IPPROTO_TCP') and\
168
+ hasattr(socket, 'TCP_KEEPIDLE') and\
169
+ hasattr(socket, 'TCP_KEEPINTVL') and\
170
+ hasattr(socket, 'TCP_KEEPCNT'):
171
+ self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
172
+ self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, keepAliveActSec)
173
+ self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, keepAliveTxSec)
174
+ self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, keepAliveFailTriggerCount)
175
+ self._socket.setblocking(False)
176
+ self._rxBuffer = bytearray()
177
+
178
+ def tx(self, theDict, throwError=True):
179
+ """@brief send a python dictionary object to the server via json
180
+ @param theDict The dictionary to send
181
+ @param throwError If True then an exception will be thrown if an error occurs.
182
+ If False then this method will fail silentley.
183
+ @return True on success. False on failure if throwError = False"""
184
+ try:
185
+ JsonServerHandler.TX(self._socket, theDict)
186
+ return True
187
+ except:
188
+ if throwError:
189
+ raise
190
+ return False
191
+
192
+ def _getDict(self):
193
+ """@brief Get dict from rx data.
194
+ @return The oldest dict in the rx buffer."""
195
+ rxDict = None
196
+ bodyLen = JsonServerHandler.GetBodyLen(self._rxBuffer)
197
+ #If we have a complete message in the RX buffer
198
+ if bodyLen > 0:
199
+ body = self._rxBuffer[JsonServerHandler.LEN_FIELD:JsonServerHandler.LEN_FIELD+bodyLen]
200
+ rxDict = JsonServerHandler.JSONToDict( body.decode() )
201
+ #Remove the message just received from the RX buffer
202
+ self._rxBuffer = self._rxBuffer[JsonServerHandler.LEN_FIELD+bodyLen:]
203
+
204
+ return rxDict
205
+
206
+ def rx(self, blocking=True,
207
+ pollPeriodSeconds=JsonServerHandler.DEFAULT_RX_POLL_SECS,
208
+ rxBufferSize=JsonServerHandler.DEFAULT_RX_BUFFER_SIZE):
209
+ """@brief Get a python dictionary object from the server.
210
+ @param blocking If True block until complete message is received.
211
+ @param pollPeriodSeconds If blocking wait for this period in seconds between checking for RX data.
212
+ @param rxBufferSize The size of the receive buffer in bytes.
213
+ @return A received dictionary of None if not blocking and no dictionary is available."""
214
+
215
+ while not JsonServerHandler.MsgAvail(self._rxBuffer):
216
+
217
+ try:
218
+ rxd = self._socket.recv(rxBufferSize)
219
+ if len(rxd) > 0:
220
+ self._rxBuffer = self._rxBuffer + rxd
221
+ else:
222
+ raise RuntimeError("Socket closed")
223
+
224
+ if not blocking:
225
+ break
226
+
227
+ except BlockingIOError:
228
+ if not blocking:
229
+ break
230
+
231
+ if blocking:
232
+ sleep(pollPeriodSeconds)
233
+
234
+ return self._getDict()
235
+
236
+ def close(self):
237
+ """@brief Close the socket connection to the server."""
238
+ if self._socket:
239
+ self._socket.close()