Add debugger

This commit is contained in:
2026-05-02 14:47:53 +02:00
parent 4f54b9be36
commit ad58fc8bc2
7 changed files with 122 additions and 88 deletions
+20 -10
View File
@@ -4,7 +4,8 @@
import atexit import atexit
import argparse import argparse
import pprint import pprint
import time
from bmspy.utilities import debugger
def parse_args(): def parse_args():
@@ -73,28 +74,37 @@ def main():
elif args.report_textfile: elif args.report_textfile:
from bmspy import prometheus from bmspy import prometheus
prometheus.prometheus_export(daemonize=False, filename=args.report_textfile, debug=debug) prometheus.prometheus_export(
daemonize=False, filename=args.report_textfile, debug=debug
)
else: elif args.report_print:
from bmspy import client from bmspy import client
client.handle_registration(args.socket, 'bmspy', debug)
atexit.register(client.handle_registration, args.socket, 'bmspy', debug) pp = pprint.PrettyPrinter(indent=4)
client.handle_registration(args.socket, "bmspy", debug)
atexit.register(client.handle_registration, args.socket, "bmspy", debug)
# {ups_name: JBDUPS} # {ups_name: JBDUPS}
data = client.read_data(args.socket, 'bmspy', ups=args.ups, debug=debug) data = client.read_data(args.socket, 'bmspy', ups=args.ups, debug=debug)
if args.report_json: if args.report_json:
import json import json
print(json.dumps({name: dict(ups.items()) for name, ups in data.items()}, default=str))
elif args.report_print: pp.pprint(
pp = pprint.PrettyPrinter(indent=4) json.dumps(
{name: dict(ups.items()) for name, ups in data.items()},
default=str,
)
)
else:
for ups_name, ups_data in data.items(): for ups_name, ups_data in data.items():
print("=== {} ===".format(ups_name)) print("=== {} ===".format(ups_name))
pp.pprint(ups_data) pp.pprint(dict(ups_data.items()))
except KeyboardInterrupt as e: except KeyboardInterrupt as e:
print(e) debugger(e)
if __name__ == '__main__': if __name__ == '__main__':
+14 -13
View File
@@ -5,6 +5,7 @@ import sys
import struct import struct
import json import json
import socket import socket
from bmspy.utilities import debugger
is_registered = False is_registered = False
@@ -33,9 +34,9 @@ def handle_registration(socket_path, client_name, debug=0):
except Exception as e: except Exception as e:
if is_registered: if is_registered:
print("{}: failed to register with daemon: {}".format(client_name, e)) debugger("{}: failed to register with daemon: {}".format(client_name, e))
else: else:
print("{}: failed to deregister with daemon: {}".format(client_name, e)) debugger("{}: failed to deregister with daemon: {}".format(client_name, e))
return data return data
@@ -48,34 +49,34 @@ def socket_comms(socket_path, request_data, debug=0):
# Connect the socket to the port where the server is listening # Connect the socket to the port where the server is listening
if debug > 2: if debug > 2:
print("socket client: connecting to {}".format(socket_path)) debugger("socket client: connecting to {}".format(socket_path))
try: try:
sock.connect(socket_path) sock.connect(socket_path)
except socket.error as msg: except socket.error as msg:
if msg.errno == 2: if msg.errno == 2:
print("Failed to connect to bmspy daemon") debugger("Failed to connect to bmspy daemon")
else: else:
print("socket client: {}".format(msg)) debugger("socket client: {}".format(msg))
# Send request # Send request
if debug > 2: if debug > 2:
print("socket client: sending {!r}".format(request_data)) debugger("socket client: sending {!r}".format(request_data))
request = bytes() request = bytes()
try: try:
request = json.dumps(request_data).encode() 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 # 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( debugger(
"socket client: outgoing request length: {}, encoded as {}".format( "socket client: outgoing request length: {}, encoded as {}".format(
len(request), length len(request), length
) )
) )
request = length + request request = length + request
if debug > 4: if debug > 4:
print("socket client: outgoing request: {}".format(request)) debugger("socket client: outgoing request: {}".format(request))
except Exception: except Exception:
print("socket client ERROR: unable to encode request") debugger("socket client ERROR: unable to encode request")
sys.exit(1) sys.exit(1)
sock.sendall(request) sock.sendall(request)
@@ -84,7 +85,7 @@ def socket_comms(socket_path, request_data, debug=0):
try: try:
length = struct.unpack("!I", response)[0] length = struct.unpack("!I", response)[0]
if debug > 4: if debug > 4:
print( debugger(
"socket client: incoming length: {}, encoded as {}".format( "socket client: incoming length: {}, encoded as {}".format(
length, response length, response
) )
@@ -92,13 +93,13 @@ def socket_comms(socket_path, request_data, debug=0):
# read length bytes # read length bytes
response = sock.recv(length) response = sock.recv(length)
if debug > 3: if debug > 3:
print("socket client: incoming response: {}".format(response)) debugger("socket client: incoming response: {}".format(response))
response_data = json.loads(response) response_data = json.loads(response)
except Exception: except Exception:
print("socket client ERROR: unable to decode response") debugger("socket client ERROR: unable to decode response")
sys.exit(1) sys.exit(1)
if debug > 2: if debug > 2:
print("socket client: received {!r}".format(response_data)) debugger("socket client: received {!r}".format(response_data))
sock.close() sock.close()
+9 -8
View File
@@ -1,6 +1,7 @@
import atexit, datetime, os, sys, time import atexit, datetime, os, sys, time
from influxdb_client_3 import InfluxDBClient3, Point from influxdb_client_3 import InfluxDBClient3, Point
from bmspy import client from bmspy import client
from bmspy.utilities import debugger
DAEMON_UPDATE_PERIOD = 30 DAEMON_UPDATE_PERIOD = 30
@@ -34,14 +35,14 @@ def influxdb_export(bucket, url=None, org=None, token=None, socket_path=None, up
def influxdb_write_snapshot(influxclient, bucket, ups_data, debug=0): def influxdb_write_snapshot(influxclient, bucket, ups_data, debug=0):
if debug > 1: if debug > 1:
print("influxdb: creating snapshot") debugger("influxdb: creating snapshot")
points = influxdb_create_snapshot(ups_data, debug) points = influxdb_create_snapshot(ups_data, debug)
if debug > 1: if debug > 1:
print("influxdb: writing snapshot") debugger("influxdb: writing snapshot")
try: try:
influxclient.write(record=points, database=bucket) influxclient.write(record=points, database=bucket)
except Exception as e: except Exception as e:
print(e) debugger(e)
def influxdb_create_snapshot(ups_data, debug=0): def influxdb_create_snapshot(ups_data, debug=0):
@@ -57,7 +58,7 @@ def influxdb_create_snapshot(ups_data, debug=0):
if contains.get('raw_value') is not None: if contains.get('raw_value') is not None:
value = contains.get('raw_value') value = contains.get('raw_value')
if debug > 2: if debug > 2:
print("value: {} [{}] : {}".format(kind, ups_name, value)) debugger("value: {} [{}] : {}".format(kind, ups_name, value))
points.append( points.append(
Point(kind) Point(kind)
.tag("ups", ups_name) .tag("ups", ups_name)
@@ -71,7 +72,7 @@ def influxdb_create_snapshot(ups_data, debug=0):
label = contains.get('label') label = contains.get('label')
for idx, label_value in contains.get('raw_values').items(): for idx, label_value in contains.get('raw_values').items():
if debug > 2: if debug > 2:
print("labels: {} [{}][{}] : {}".format(kind, ups_name, idx, label_value)) debugger("labels: {} [{}][{}] : {}".format(kind, ups_name, idx, label_value))
points.append( points.append(
Point(kind) Point(kind)
.tag("ups", ups_name) .tag("ups", ups_name)
@@ -85,7 +86,7 @@ def influxdb_create_snapshot(ups_data, debug=0):
if contains.get('info') is not None: if contains.get('info') is not None:
value = contains.get('info') value = contains.get('info')
if debug > 2: if debug > 2:
print("info: {} [{}] : {}".format(kind, ups_name, value)) debugger("info: {} [{}] : {}".format(kind, ups_name, value))
points.append( points.append(
Point(kind) Point(kind)
.tag("ups", ups_name) .tag("ups", ups_name)
@@ -131,10 +132,10 @@ def main():
if not os.getenv('INFLUXDB_V2_TOKEN') and not args.influx_token: if not os.getenv('INFLUXDB_V2_TOKEN') and not args.influx_token:
raise argparse.ArgumentTypeError('Missing value for --token') raise argparse.ArgumentTypeError('Missing value for --token')
except Exception as e: except Exception as e:
print("bmspy-influxdb: {}".format(e)) debugger("bmspy-influxdb: {}".format(e))
sys.exit(1) sys.exit(1)
print("Running BMS influxdb daemon on socket {}".format(args.socket)) debugger("Running BMS influxdb daemon on socket {}".format(args.socket))
client.handle_registration(args.socket, 'influxdb', debug) client.handle_registration(args.socket, 'influxdb', debug)
atexit.register(client.handle_registration, args.socket, 'influxdb', debug) atexit.register(client.handle_registration, args.socket, 'influxdb', debug)
+40 -39
View File
@@ -8,6 +8,7 @@ import serial.rs485
import time import time
from dataclasses import dataclass, fields as dataclass_fields from dataclasses import dataclass, fields as dataclass_fields
from bmspy.utilities import debugger
from bmspy.classes import BMSScalarField, BMSMultiField, BMSInfoField from bmspy.classes import BMSScalarField, BMSMultiField, BMSInfoField
@@ -51,7 +52,7 @@ class JBDUPS:
def serial_cleanup(ser, debug=0): def serial_cleanup(ser, debug=0):
if debug > 2: if debug > 2:
print("serial: cleaning up...") debugger("serial: cleaning up...")
if ser.is_open: if ser.is_open:
ser.reset_input_buffer() ser.reset_input_buffer()
ser.reset_output_buffer() ser.reset_output_buffer()
@@ -114,14 +115,14 @@ def bytes_to_date(high, low):
def requestMessage(ser, reqmsg, debug=0): def requestMessage(ser, reqmsg, debug=0):
if debug > 2: if debug > 2:
print("serial: starting up monitor") debugger("serial: starting up monitor")
if ser.is_open: if ser.is_open:
ser.close() ser.close()
try: try:
ser.open() ser.open()
except Exception as e: except Exception as e:
print("serial: error open port: {0}".format(str(e))) debugger("serial: error open port: {0}".format(str(e)))
return False return False
if ser.is_open: if ser.is_open:
@@ -132,16 +133,16 @@ def requestMessage(ser, reqmsg, debug=0):
ser.reset_input_buffer() ser.reset_input_buffer()
ser.reset_output_buffer() ser.reset_output_buffer()
if debug > 0: if debug > 0:
print( debugger(
"serial: write data: {0}".format( "serial: write data: {0}".format(
"".join("0x{:02x} ".format(x) for x in reqmsg) "".join("0x{:02x} ".format(x) for x in reqmsg)
) )
) )
w = ser.write(reqmsg) w = ser.write(reqmsg)
if debug > 2: if debug > 2:
print("serial: bytes written: {0}".format(w)) debugger("serial: bytes written: {0}".format(w))
if w != len(reqmsg): if w != len(reqmsg):
print( debugger(
"serial ERROR: {0} bytes written, {1} expected.".format( "serial ERROR: {0} bytes written, {1} expected.".format(
w, len(reqmsg) w, len(reqmsg)
) )
@@ -153,22 +154,22 @@ def requestMessage(ser, reqmsg, debug=0):
serial_cleanup(ser, debug) serial_cleanup(ser, debug)
return "" return ""
if debug > 2: if debug > 2:
print("serial: waiting for data...") debugger("serial: waiting for data...")
time.sleep(0.5) time.sleep(0.5)
wait_time += 1 wait_time += 1
if debug > 1: if debug > 1:
print("serial: waiting reading: {0}".format(ser.in_waiting)) debugger("serial: waiting reading: {0}".format(ser.in_waiting))
response = ser.read_until(b"\x77") response = ser.read_until(b"\x77")
if len(response) == 0: if len(response) == 0:
return "" return ""
if debug > 0: if debug > 0:
print("serial: read data: {0}".format(response.hex())) debugger("serial: read data: {0}".format(response.hex()))
serial_cleanup(ser, debug) serial_cleanup(ser, debug)
return response return response
except Exception as e: except Exception as e:
print("serial: error communicating: {0}".format(str(e))) debugger("serial: error communicating: {0}".format(str(e)))
else: else:
print("serial: cannot open port") debugger("serial: cannot open port")
def parse_03_response(response, debug=0): def parse_03_response(response, debug=0):
@@ -183,11 +184,11 @@ def parse_03_response(response, debug=0):
# length+5 checksum # length+5 checksum
# length+6 end \x77 # length+6 end \x77
if bytes([response[0]]) != b"\xdd": if bytes([response[0]]) != b"\xdd":
print("parse_03_response ERROR: first byte not found: {0}".format(response[0])) debugger("parse_03_response ERROR: first byte not found: {0}".format(response[0]))
return False return False
if bytes([response[2]]) == b"\x80": if bytes([response[2]]) == b"\x80":
print( debugger(
"parse_03_response ERROR: error byte returned from BMS: {0}".format( "parse_03_response ERROR: error byte returned from BMS: {0}".format(
response[2] response[2]
) )
@@ -196,7 +197,7 @@ def parse_03_response(response, debug=0):
data_len = response[3] data_len = response[3]
if debug > 2: if debug > 2:
print("parse_03_response: data length (trimming 4 bytes): {0}".format(data_len)) debugger("parse_03_response: data length (trimming 4 bytes): {0}".format(data_len))
# The checksum is two bytes, offset by data_len + 4 # The checksum is two bytes, offset by data_len + 4
# Five bytes at the front of data: begin; rw; status, command; length # Five bytes at the front of data: begin; rw; status, command; length
@@ -204,11 +205,11 @@ def parse_03_response(response, debug=0):
first = data_len + 4 first = data_len + 4
second = data_len + 5 second = data_len + 5
if second > len(response): if second > len(response):
print("parse_03_response ERROR: primary response checksum not found") debugger("parse_03_response ERROR: primary response checksum not found")
return False return False
checksum = bytes([response[first], response[second]]) checksum = bytes([response[first], response[second]])
if not verify_checksum(response[3:first], checksum): if not verify_checksum(response[3:first], checksum):
print("parse_03_response ERROR: failed to validate received checksum") debugger("parse_03_response ERROR: failed to validate received checksum")
return False return False
if data_len == 0: if data_len == 0:
@@ -221,14 +222,14 @@ def parse_03_response(response, debug=0):
help="Total Voltage", raw_value=vtot, value="{:.2f}".format(vtot), units="V" help="Total Voltage", raw_value=vtot, value="{:.2f}".format(vtot), units="V"
) )
if debug > 1: if debug > 1:
print(" Total voltage: {:.2f}V".format(vtot)) debugger(" Total voltage: {:.2f}V".format(vtot))
current = convert_to_signed(bytes_to_digits(response[6], response[7])) * 0.01 current = convert_to_signed(bytes_to_digits(response[6], response[7])) * 0.01
result.bms_current_amps = BMSScalarField( result.bms_current_amps = BMSScalarField(
help="Current", raw_value=current, value="{:.2f}".format(current), units="A" help="Current", raw_value=current, value="{:.2f}".format(current), units="A"
) )
if debug > 1: if debug > 1:
print(" Current: {:.2f}A".format(current)) debugger(" Current: {:.2f}A".format(current))
res_cap = bytes_to_digits(response[8], response[9]) * 0.01 res_cap = bytes_to_digits(response[8], response[9]) * 0.01
nom_cap = bytes_to_digits(response[10], response[11]) * 0.01 nom_cap = bytes_to_digits(response[10], response[11]) * 0.01
@@ -245,29 +246,29 @@ def parse_03_response(response, debug=0):
units="Ah", units="Ah",
) )
if debug > 1: if debug > 1:
print(" Remaining capacity: {:.2f}Ah".format(res_cap)) debugger(" Remaining capacity: {:.2f}Ah".format(res_cap))
print(" Nominal capacity: {:.2f}Ah".format(nom_cap)) debugger(" Nominal capacity: {:.2f}Ah".format(nom_cap))
cycle_times = bytes_to_digits(response[12], response[13]) cycle_times = bytes_to_digits(response[12], response[13])
result.bms_charge_cycles = BMSScalarField( result.bms_charge_cycles = BMSScalarField(
help="Charge Cycles", raw_value=cycle_times, value="{0}".format(cycle_times) help="Charge Cycles", raw_value=cycle_times, value="{0}".format(cycle_times)
) )
if debug > 1: if debug > 1:
print(" Cycle times: {0}".format(cycle_times)) debugger(" Cycle times: {0}".format(cycle_times))
man_date = bytes_to_date(response[14], response[15]) man_date = bytes_to_date(response[14], response[15])
result.bms_manufacture_date = BMSInfoField( result.bms_manufacture_date = BMSInfoField(
help="Date of Manufacture", info="{0}".format(man_date) help="Date of Manufacture", info="{0}".format(man_date)
) )
if debug > 1: if debug > 1:
print(" Manufacturing date: {0}".format(man_date)) debugger(" Manufacturing date: {0}".format(man_date))
cells = response[25] cells = response[25]
result.bms_cell_number = BMSScalarField( result.bms_cell_number = BMSScalarField(
help="Cells", raw_value=cells, value="{0}".format(cells) help="Cells", raw_value=cells, value="{0}".format(cells)
) )
if debug > 1: if debug > 1:
print(" Cells: {0}S".format(cells)) debugger(" Cells: {0}S".format(cells))
balance_state_high = bytes_to_digits(response[16], response[17]) # 1S to 16S balance_state_high = bytes_to_digits(response[16], response[17]) # 1S to 16S
balance_state_low = bytes_to_digits(response[18], response[19]) # 17S to 32S balance_state_low = bytes_to_digits(response[18], response[19]) # 17S to 32S
@@ -306,7 +307,7 @@ def parse_03_response(response, debug=0):
raw_balancing[cell + 1] = balancing raw_balancing[cell + 1] = balancing
str_balancing[cell + 1] = "{0}".format(int(balancing)) str_balancing[cell + 1] = "{0}".format(int(balancing))
if debug > 1: if debug > 1:
print(" Balancing cell {0}: {1}".format(cell, balancing)) debugger(" Balancing cell {0}: {1}".format(cell, balancing))
result.bms_cells_balancing = BMSMultiField( result.bms_cells_balancing = BMSMultiField(
help="Cells balancing", help="Cells balancing",
label="cell", label="cell",
@@ -355,7 +356,7 @@ def parse_03_response(response, debug=0):
result.bms_protection_slmos_bool = _prot("Software lock MOS", slm) result.bms_protection_slmos_bool = _prot("Software lock MOS", slm)
if debug > 2: if debug > 2:
print(" Protection state: {0}".format(protection_state)) debugger(" Protection state: {0}".format(protection_state))
for attr in ( for attr in (
"sop", "sop",
"sup", "sup",
@@ -372,14 +373,14 @@ def parse_03_response(response, debug=0):
"slm", "slm",
): ):
val = locals()[attr] val = locals()[attr]
print(" {}: {}".format(attr, bool(val))) debugger(" {}: {}".format(attr, bool(val)))
rsoc = response[23] * 0.01 rsoc = response[23] * 0.01
result.bms_capacity_charge_ratio = BMSScalarField( result.bms_capacity_charge_ratio = BMSScalarField(
help="Percent Charge", raw_value=rsoc, value="{0}".format(rsoc), units="" help="Percent Charge", raw_value=rsoc, value="{0}".format(rsoc), units=""
) )
if debug > 1: if debug > 1:
print(" Capacity remaining: {0}%".format(rsoc * 100)) debugger(" Capacity remaining: {0}%".format(rsoc * 100))
# bit0 = charging; bit1 = discharging; 0 = MOS closing; 1 = MOS opening # bit0 = charging; bit1 = discharging; 0 = MOS closing; 1 = MOS opening
control_status = response[24] control_status = response[24]
@@ -394,8 +395,8 @@ def parse_03_response(response, debug=0):
value="{0}".format(int(bool((control_status >> 1) & 1))), value="{0}".format(int(bool((control_status >> 1) & 1))),
) )
if debug > 1: if debug > 1:
print(" MOSFET charging: {0}".format("yes" if (control_status & 1) else "no")) debugger(" MOSFET charging: {0}".format("yes" if (control_status & 1) else "no"))
print( debugger(
" MOSFET discharging: {0}".format( " MOSFET discharging: {0}".format(
"yes" if ((control_status >> 1) & 1) else "no" "yes" if ((control_status >> 1) & 1) else "no"
) )
@@ -417,9 +418,9 @@ def parse_03_response(response, debug=0):
units="°C", units="°C",
) )
if debug > 1: if debug > 1:
print(" Number of temperature sensors: {0}".format(ntc_num)) debugger(" Number of temperature sensors: {0}".format(ntc_num))
for i, temp in enumerate(temperatures): for i, temp in enumerate(temperatures):
print(" Temperature sensor {:d}: {:.2f}°C".format(i + 1, temp)) debugger(" Temperature sensor {:d}: {:.2f}°C".format(i + 1, temp))
return result return result
@@ -436,11 +437,11 @@ def parse_04_response(response, debug=0):
# length+5 checksum # length+5 checksum
# length+6 end \x77 # length+6 end \x77
if bytes([response[0]]) != b"\xdd": if bytes([response[0]]) != b"\xdd":
print("parse_04_response ERROR: first byte not found: {0}".format(response[0])) debugger("parse_04_response ERROR: first byte not found: {0}".format(response[0]))
return False return False
if bytes([response[2]]) == b"\x80": if bytes([response[2]]) == b"\x80":
print( debugger(
"parse_04_response ERROR: error byte returned from BMS: {0}".format( "parse_04_response ERROR: error byte returned from BMS: {0}".format(
response[2] response[2]
) )
@@ -449,7 +450,7 @@ def parse_04_response(response, debug=0):
data_len = response[3] data_len = response[3]
if debug > 2: if debug > 2:
print(" Data length (trimming 4 bytes): {0}".format(data_len)) debugger(" Data length (trimming 4 bytes): {0}".format(data_len))
# The checksum is two bytes, offset by data_len + 4 # The checksum is two bytes, offset by data_len + 4
# Five bytes at the front of data: begin; rw; status, command; length # Five bytes at the front of data: begin; rw; status, command; length
@@ -457,11 +458,11 @@ def parse_04_response(response, debug=0):
first = data_len + 4 first = data_len + 4
second = data_len + 5 second = data_len + 5
if second > len(response): if second > len(response):
print("parse_04_response ERROR: cell voltage checksum not found") debugger("parse_04_response ERROR: cell voltage checksum not found")
return False return False
checksum = bytes([response[first], response[second]]) checksum = bytes([response[first], response[second]])
if not verify_checksum(response[3:first], checksum): if not verify_checksum(response[3:first], checksum):
print("parse_04_response ERROR: failed to validate received checksum") debugger("parse_04_response ERROR: failed to validate received checksum")
return False return False
if data_len == 0: if data_len == 0:
@@ -476,7 +477,7 @@ def parse_04_response(response, debug=0):
raw_values[cell + 1] = cellv raw_values[cell + 1] = cellv
str_values[cell + 1] = "{:.3f}".format(cellv) str_values[cell + 1] = "{:.3f}".format(cellv)
if debug > 1: if debug > 1:
print(" Cell {:.0f}: {:.3f}V".format(cell + 1, cellv)) debugger(" Cell {:.0f}: {:.3f}V".format(cell + 1, cellv))
return BMSMultiField( return BMSMultiField(
help="Cell Voltages", help="Cell Voltages",
@@ -498,7 +499,7 @@ def collect_data(ser, debug=0):
if len(response_03) == 0: if len(response_03) == 0:
if debug > 0: if debug > 0:
print("collect_data: Error retrieving BMS info. Trying again...") debugger("collect_data: Error retrieving BMS info. Trying again...")
return False return False
response_03 = bytearray(response_03) response_03 = bytearray(response_03)
@@ -507,7 +508,7 @@ def collect_data(ser, debug=0):
if len(response_04) == 0: if len(response_04) == 0:
if debug > 0: if debug > 0:
print("collect_data: Error retrieving BMS info. Trying again...") debugger("collect_data: Error retrieving BMS info. Trying again...")
return False return False
response_04 = bytearray(response_04) response_04 = bytearray(response_04)
+5 -3
View File
@@ -1,4 +1,6 @@
import prometheus_client import prometheus_client
from bmspy.utilities import debugger
def prometheus_export(daemonize=True, filename=None): def prometheus_export(daemonize=True, filename=None):
global debug global debug
@@ -32,7 +34,7 @@ def prometheus_export(daemonize=True, filename=None):
prometheus_client.generate_latest(registry) prometheus_client.generate_latest(registry)
else: else:
if filename is None: if filename is None:
print("Invalid filename supplied"); debugger("Invalid filename supplied");
return False return False
prometheus_client.write_to_textfile(filename, registry=registry) prometheus_client.write_to_textfile(filename, registry=registry)
return True return True
@@ -50,7 +52,7 @@ def prometheus_create_metric(registry, data):
# Has multiple values, each a different label # Has multiple values, each a different label
elif contains.get('values') is not None: elif contains.get('values') is not None:
if contains.get('label') is None: if contains.get('label') is None:
print("ERROR: no label for {0} specified".format(name)) debugger("ERROR: no label for {0} specified".format(name))
label = contains.get('label') label = contains.get('label')
metric[name] = prometheus_client.Gauge(name, helpmsg, [label], registry=registry) metric[name] = prometheus_client.Gauge(name, helpmsg, [label], registry=registry)
elif contains.get('info') is not None: elif contains.get('info') is not None:
@@ -78,7 +80,7 @@ def prometheus_populate_metric(metric, data):
# TODO fork bms daemon if need be? # TODO fork bms daemon if need be?
def main(): def main():
print("TODO. At present, run from bmspy directly.") debugger("TODO. At present, run from bmspy directly.")
# influxdb_export(bucket=args.influx_bucket, \ # influxdb_export(bucket=args.influx_bucket, \
# url=args.influx_url, \ # url=args.influx_url, \
+15 -15
View File
@@ -12,6 +12,7 @@ import json
import struct import struct
from dataclasses import asdict as dataclass_asdict from dataclasses import asdict as dataclass_asdict
from bmspy.utilities import debugger
from bmspy.jbd_ups import collect_data, initialise_serial from bmspy.jbd_ups import collect_data, initialise_serial
# Expected kernel log output when the USB-serial adapter is plugged in: # Expected kernel log output when the USB-serial adapter is plugged in:
@@ -52,7 +53,7 @@ def read_request(connection, debug=0):
except Exception as e: except Exception as e:
raise Exception("unable to determine request length: {}".format(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)) debugger("socket: incoming length: {}, encoded as {}".format(length, request))
# read length bytes # read length bytes
try: try:
@@ -60,22 +61,21 @@ def read_request(connection, debug=0):
except Exception as e: except Exception as e:
raise OSError("unable to read socket: {}".format(e)) raise OSError("unable to read socket: {}".format(e))
if debug > 3: if debug > 3:
print("socket: incoming request: {}".format(request)) debugger("socket: incoming request: {}".format(request))
try: try:
request_data = json.loads(request) request_data = json.loads(request)
except Exception as e: except Exception as e:
raise Exception("unable to read incoming request: {}".format(e)) raise Exception("unable to read incoming request: {}".format(e))
if debug > 2: if debug > 2:
print("socket: received {!r}".format(request_data)) debugger("socket: received {!r}".format(request_data))
return request_data return request_data
def send_response(connection, response_data, client, debug=0): def send_response(connection, response_data, client, debug=0):
if debug > 2: if debug > 2:
print("socket: sending {!r}".format(response_data)) debugger("socket: sending {!r}".format(response_data))
try: try:
response = json.dumps(response_data).encode()
response = json.dumps( response = json.dumps(
response_data, response_data,
default=lambda o: {k: dataclass_asdict(v) for k, v in o.items()} default=lambda o: {k: dataclass_asdict(v) for k, v in o.items()}
@@ -164,7 +164,7 @@ def main():
for device_str in device_list: for device_str in device_list:
name, path = parse_device(device_str) name, path = parse_device(device_str)
if name in ups_devices: if name in ups_devices:
print("server: duplicate UPS name '{}', skipping {}".format(name, path)) debugger("server: duplicate UPS name '{}', skipping {}".format(name, path))
continue continue
ups_devices[name] = { ups_devices[name] = {
"ser": initialise_serial(path, debug), "ser": initialise_serial(path, debug),
@@ -175,7 +175,7 @@ def main():
print("server: registered UPS '{}' on {}".format(name, path)) print("server: registered UPS '{}' on {}".format(name, path))
if debug > 0: if debug > 0:
print("Running BMS query daemon on socket {}".format(args.socket)) debugger("Running BMS query daemon on socket {}".format(args.socket))
socket_dir = os.path.dirname(args.socket) socket_dir = os.path.dirname(args.socket)
socket_dir_created = False socket_dir_created = False
@@ -207,7 +207,7 @@ def main():
new_umask = 0o003 new_umask = 0o003
old_umask = os.umask(new_umask) old_umask = os.umask(new_umask)
if debug > 1: if debug > 1:
print( debugger(
"socket: old umask: %s, new umask: %s" "socket: old umask: %s, new umask: %s"
% (oct(old_umask), oct(new_umask)) % (oct(old_umask), oct(new_umask))
) )
@@ -215,17 +215,17 @@ def main():
try: try:
os.setgid(running_gid) os.setgid(running_gid)
except OSError as e: except OSError as e:
print("could not set effective group id: {}".format(e)) debugger("could not set effective group id: {}".format(e))
try: try:
os.setuid(running_uid) os.setuid(running_uid)
except OSError as e: except OSError as e:
print("could not set effective user id: {}".format(e)) debugger("could not set effective user id: {}".format(e))
final_uid = os.getuid() final_uid = os.getuid()
final_gid = os.getgid() final_gid = os.getgid()
if debug > 0: if debug > 0:
print( debugger(
"socket: running as {}:{}".format( "socket: running as {}:{}".format(
pwd.getpwuid(final_uid)[0], grp.getgrgid(final_gid)[0] pwd.getpwuid(final_uid)[0], grp.getgrgid(final_gid)[0]
) )
@@ -237,7 +237,7 @@ def main():
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
if debug > 2: if debug > 2:
print("starting up on {}".format(args.socket)) debugger("starting up on {}".format(args.socket))
sock.bind(args.socket) sock.bind(args.socket)
atexit.register(socket_cleanup, args.socket, debug) atexit.register(socket_cleanup, args.socket, debug)
@@ -249,7 +249,7 @@ def main():
try: try:
if debug > 2: if debug > 2:
print("socket: waiting for a connection") debugger("socket: waiting for a connection")
connection, client_address = sock.accept() connection, client_address = sock.accept()
request_data = dict() request_data = dict()
@@ -294,7 +294,7 @@ def main():
result = {} result = {}
for name, device in targets.items(): for name, device in targets.items():
if debug > 0: if debug > 0:
print( debugger(
"reading data for '{}', timestamp={}, time={}".format( "reading data for '{}', timestamp={}, time={}".format(
name, device["timestamp"], time.time() name, device["timestamp"], time.time()
) )
@@ -311,7 +311,7 @@ def main():
send_response(connection, result, client, debug) send_response(connection, result, client, debug)
case _: case _:
print( debugger(
"socket: invalid request from {}".format(request_data["client"]) "socket: invalid request from {}".format(request_data["client"])
) )
break break
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env python3
#
# Daemon: listens on a Unix socket and serves JBD BMS data to clients
#
import datetime
import pprint
def debugger(data, pretty: bool = False):
if pretty:
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(
{
"time": datetime.datetime.now(),
"data": data,
}
)
msg = f"{str(datetime.datetime.now())} {data}"
print(msg)