Use JSON for serialising requests/responses, rather than pickle, to make C integration easier

This commit is contained in:
Timothy Allen 2024-05-09 09:42:59 +02:00
parent dab791fb79
commit cae4d1b0a4
4 changed files with 70 additions and 37 deletions

View File

@ -2,7 +2,7 @@
# Library with socket client for use by consumers # Library with socket client for use by consumers
# #
import atexit, os, sys import atexit, os, sys
import pickle, struct import struct, json
import socket import socket
@ -55,18 +55,24 @@ def socket_comms(socket_path, request_data, debug=0):
# Send request # Send request
if debug > 2: if debug > 2:
print('socket client: sending {!r}'.format(request_data)) print('socket client: sending {!r}'.format(request_data))
request = pickle.dumps(request_data) request = bytes()
# add length to the start of the pickled bytes, so we know how much to read on the other end try:
request = json.dumps(request_data).encode()
# add length to the start of the json string, so we know how much to read on the other end
length = struct.pack('!I', len(request)) length = struct.pack('!I', len(request))
if debug > 3: if debug > 3:
print("socket client: outgoing request length: {}, encoded as {}".format(len(request), length)) print("socket client: outgoing request length: {}, encoded as {}".format(len(request), length))
request = length + request request = length + request
if debug > 4: if debug > 4:
print("socket client: outgoing request: {}".format(request)) print("socket client: outgoing request: {}".format(request))
except:
print("socket client ERROR: unable to encode request")
sys.exit(1)
sock.sendall(request) sock.sendall(request)
# get length of expected pickled bytes # get length of expected json string
response = sock.recv(struct.calcsize('!I')) response = sock.recv(struct.calcsize('!I'))
try:
length = struct.unpack('!I', response)[0] length = struct.unpack('!I', response)[0]
if debug > 4: if debug > 4:
print("socket client: incoming length: {}, encoded as {}".format(length, response)) print("socket client: incoming length: {}, encoded as {}".format(length, response))
@ -74,7 +80,10 @@ def socket_comms(socket_path, request_data, debug=0):
response = sock.recv(length) response = sock.recv(length)
if debug > 3: if debug > 3:
print("socket client: incoming response: {}".format(response)) print("socket client: incoming response: {}".format(response))
response_data = pickle.loads(response) response_data = json.loads(response)
except:
print("socket client ERROR: unable to decode response")
sys.exit(1)
if debug > 2: if debug > 2:
print('socket client: received {!r}'.format(response_data)) print('socket client: received {!r}'.format(response_data))

View File

@ -65,7 +65,7 @@ def influxdb_create_snapshot(data, debug=0):
for kind, contains in data.items(): for kind, contains in data.items():
# discard bmspy metadata # discard bmspy metadata
if kind == 'client': if kind == 'client' or kind == 'timestamp':
break break
helpmsg = '' helpmsg = ''

View File

@ -8,7 +8,7 @@ import os, sys, stat, time
import json import json
import atexit, signal import atexit, signal
import serial, serial.rs485 import serial, serial.rs485
import pickle, struct import struct, json
import pprint import pprint
connected_clients = list() connected_clients = list()
@ -527,17 +527,30 @@ def collect_data(ser, debug=0):
def read_request(connection, debug=0): def read_request(connection, debug=0):
# get length of expected pickle bytes # get length of expected json string
request = bytes()
try:
request = connection.recv(struct.calcsize('!I')) request = connection.recv(struct.calcsize('!I'))
except Exception as e:
raise OSError("unable to read request length from socket: {}".format(e))
try:
length = struct.unpack('!I', request)[0] length = struct.unpack('!I', request)[0]
except Exception as e:
raise Exception("unable to determine request length: {}".format(e))
if debug > 4: if debug > 4:
print("socket: incoming length: {}, encoded as {}".format(length, request)) print("socket: incoming length: {}, encoded as {}".format(length, request))
# read length bytes # read length bytes
try:
request = connection.recv(length) request = connection.recv(length)
except Exception as e:
raise OSError("unable to read socket: {}".format(e))
if debug > 3: if debug > 3:
print("socket: incoming request: {}".format(request)) print("socket: incoming request: {}".format(request))
request_data = pickle.loads(request) try:
request_data = json.loads(request)
except Exception as e:
raise Exception("unable to read incoming request: {}".format(e))
if debug > 2: if debug > 2:
print('socket: received {!r}'.format(request_data)) print('socket: received {!r}'.format(request_data))
@ -552,8 +565,9 @@ def send_response(connection, response_data, debug=0):
if debug > 2: if debug > 2:
print('socket: sending {!r}'.format(response_data)) print('socket: sending {!r}'.format(response_data))
response = pickle.dumps(response_data) try:
# add length to the start of the pickled bytes, so we know how much to read on the other end response = json.dumps(response_data).encode()
# add length to the start of the json string, so we know how much to read on the other end
length = struct.pack('!I', len(response)) length = struct.pack('!I', len(response))
if debug > 4: if debug > 4:
print('socket: sending {} data of length: {}'.format(client, length)) print('socket: sending {} data of length: {}'.format(client, length))
@ -561,6 +575,8 @@ def send_response(connection, response_data, debug=0):
if debug > 3: if debug > 3:
print("socket: outgoing response: {}".format(response)) print("socket: outgoing response: {}".format(response))
return connection.sendall(response) return connection.sendall(response)
except Exception as e:
raise OSError("unable to encode response: {}".format(e))
def main(): def main():
@ -664,7 +680,12 @@ def main():
connection, client_address = sock.accept() connection, client_address = sock.accept()
request_data = dict() request_data = dict()
try:
request_data = read_request(connection, debug) request_data = read_request(connection, debug)
except Exception as e:
print("socket ERROR: {}".format(e))
continue
client = request_data['client'] or 'unknown' client = request_data['client'] or 'unknown'
match request_data['command']: match request_data['command']:
@ -682,6 +703,9 @@ def main():
send_response(connection, {'status': 'DEREGISTERED', 'client': client}, debug) send_response(connection, {'status': 'DEREGISTERED', 'client': client}, debug)
case 'GET': case 'GET':
timestamp = 0
if bool(current_data) is True:
timestamp = current_data.get('timestamp', 0)
print("reading data, current timestamp is {}, time is {}".format(timestamp, time.time())) print("reading data, current timestamp is {}, time is {}".format(timestamp, time.time()))
# only get new data five seconds after the last read # only get new data five seconds after the last read
if timestamp <= time.time() - 5: if timestamp <= time.time() - 5:
@ -690,7 +714,7 @@ def main():
while bool(current_data) is False: while bool(current_data) is False:
current_data = collect_data(ser, debug) current_data = collect_data(ser, debug)
time.sleep(1) time.sleep(1)
timestamp = time.time() current_data['timestamp'] = time.time()
current_data['client'] = client current_data['client'] = client
send_response(connection, current_data, debug) send_response(connection, current_data, debug)

View File

@ -25,4 +25,4 @@ bmspy = "bmspy:main"
bmspy-server = "bmspy.server:main" bmspy-server = "bmspy.server:main"
bmspy-influxdb = "bmspy.influxdb:main" bmspy-influxdb = "bmspy.influxdb:main"
bmspy-prometheus = "bmspy.prometheus:main" bmspy-prometheus = "bmspy.prometheus:main"
#bmspy-usbd = "bmspy.usbhid:main" bmspy-usbd = "bmspy.usbhid:main"