diff --git a/common/tools/field_test_logger/S95logger b/common/tools/field_test_logger/S95logger index 7122184a2..92e2a99a8 100644 --- a/common/tools/field_test_logger/S95logger +++ b/common/tools/field_test_logger/S95logger @@ -5,7 +5,7 @@ name="field_test_logger" # The path of the client executable command="/usr/bin/python3" # Any command line arguments for the client executable -command_args="/usr/local/bin/field_test_logger.py $2" +command_args="/usr/local/bin/field_test_logger.py -u $2" # The path of the daemon executable daemon="/usr/bin/daemon" diff --git a/common/tools/field_test_logger/field_test_logger.py b/common/tools/field_test_logger/field_test_logger.py index 801b1406c..6deeac97f 100644 --- a/common/tools/field_test_logger/field_test_logger.py +++ b/common/tools/field_test_logger/field_test_logger.py @@ -1,7 +1,7 @@ import csv import os import time -import sys +import argparse from datetime import datetime import wifi_info @@ -94,12 +94,40 @@ def timestamp() -> str: if __name__ == '__main__': + argparse = argparse.ArgumentParser(description='Field Test Logger', + prog='field_test_logger.py') + argparse.add_argument('-u', '--unique', help='Add unique suffix to log file name') + argparse.add_argument('-i', '--interface', + help='Specify wifi interface name e.g. wlp1s0 (default: wlp1s0)') + argparse.add_argument('-b', '--batman', + help="Specify batman interface name e.g. bat0 (default: bat0)") + + args = argparse.parse_args() + + if args.batman is None: + BATMAN_ARG = "bat0" + else: + BATMAN_ARG = args.batman + + if args.interface is None: + INTERFACE_ARG = "wlp1s0" + else: + INTERFACE_ARG = args.interface + + if args.unique is None or args.unique == "": + UNIQUE_ARG = f"_{INTERFACE_ARG}_{BATMAN_ARG}" + else: + UNIQUE_ARG = f"_{INTERFACE_ARG}_{BATMAN_ARG}_{args.unique}" + + try: + with open("/etc/comms_pcb_version", "r") as file_r: + PCB_VERSION = file_r.read().strip().split("=")[1] + except FileNotFoundError: + PCB_VERSION = "unknown" + ftl = FieldTestLogger() - wifi_stats = wifi_info.WifiInfo(LOGGING_INTERVAL_SECONDS) - info = infoparser.InfoParser() - uc_arg = "" - if len(sys.argv) > 1: - uc_arg = f"_{sys.argv[1]}" + wifi_stats = wifi_info.WifiInfo(LOGGING_INTERVAL_SECONDS, INTERFACE_ARG, BATMAN_ARG) + info = infoparser.InfoParser(PCB_VERSION) ftl.register_logger_function("Timestamp", timestamp) wifi_stats.update() @@ -131,6 +159,17 @@ def timestamp() -> str: ftl.register_logger_function("wifi temp [mC]", info.get_wifi_temp) ftl.register_logger_function("tmp100 [mC]", info.get_tmp100) + ftl.register_logger_function("3V3 mpcie3 voltage [mV]", info.get_mpcie3_voltage) + ftl.register_logger_function("3V3 mpcie3 current [mA]", info.get_mpcie3_current) + ftl.register_logger_function("3V3 mpcie5 voltage [mV]", info.get_mpcie5_voltage) + ftl.register_logger_function("3V3 mpcie5 current [mA]", info.get_mpcie5_current) + ftl.register_logger_function("3V3 mpcie7 voltage [mV]", info.get_mpcie7_voltage) + ftl.register_logger_function("3V3 mpcie7 current [mA]", info.get_mpcie7_current) + + ftl.register_logger_function("bme280 rel. humidity [m%]", info.get_humidity) + ftl.register_logger_function("bme280 pressure [kPa]", info.get_pressure) + ftl.register_logger_function("bme280 temperature [mC]", info.get_temperature) + ftl.register_logger_function("battery voltage [uV]", info.get_battery_voltage) ftl.register_logger_function("battery current [uA]", info.get_battery_current) ftl.register_logger_function("nRF voltage [mV]", info.get_nrf_voltage) @@ -140,7 +179,7 @@ def timestamp() -> str: ftl.register_logger_function("DCin (XT30) voltage [mV]", info.get_dc_voltage) ftl.register_logger_function("DCin (XT30) current [mA]", info.get_dc_current) - ftl.create_csv(f"{wifi_stats.get_mac_addr()}{uc_arg}") + ftl.create_csv(f"{wifi_stats.get_mac_addr()}{UNIQUE_ARG}") while True: start = time.time() @@ -151,4 +190,4 @@ def timestamp() -> str: # adjust delay for precise logging interval if d < LOGGING_INTERVAL_SECONDS: - time.sleep(LOGGING_INTERVAL_SECONDS - d) \ No newline at end of file + time.sleep(LOGGING_INTERVAL_SECONDS - d) diff --git a/common/tools/field_test_logger/infoparser.py b/common/tools/field_test_logger/infoparser.py index c94a0a923..d92b83d44 100644 --- a/common/tools/field_test_logger/infoparser.py +++ b/common/tools/field_test_logger/infoparser.py @@ -1,11 +1,17 @@ +""" +InfoParser class is used to parse the information from the system and GPSD. +""" import glob import gpsd def read_value(file: str) -> str: + """ + Read a single value from a file. + """ try: - with open(file, 'r') as f: - value = f.readline() + with open(file, 'r') as file_handle: + value = file_handle.readline() return value.strip() @@ -14,28 +20,40 @@ def read_value(file: str) -> str: def get_hwmon_path_from_options(paths: [str]) -> str: + """ + Get the path to the hwmon directory. + """ for path in paths: - p = get_hwmon_path(path) - if p != "NaN": - return p + path_to_validate = get_hwmon_path(path) + if path_to_validate != "NaN": + return path_to_validate return "NaN" def get_hwmon_path(path: str) -> str: + """ + Get the path to the hwmon directory. + """ # hwmon directory should contain only one entry which is of format hwmon* try: return glob.glob(path)[0] except: return "NaN" - -# ---------------------------------------- - - +#pylint: disable=too-many-instance-attributes, too-many-public-methods class InfoParser: + """ + InfoParser class is used to parse the information from the system and GPSD. + """ - def __init__(self): - self.__gpsdConnected = False + def __init__(self, pcb_version: str): + """ + Initialize the InfoParser class. + """ + self.__pcb_version = pcb_version + # + self.__gpsd_connected = False + self.__max17260_connected = True # self.__altitude = 0 self.__latitude = -999999 @@ -47,6 +65,7 @@ def __init__(self): self.__track = 0 # self.__cpu_temp = "NaN" + self.__bat_temp = "NaN" self.__wifi_temp = "NaN" self.__tmp100 = "NaN" # @@ -59,105 +78,283 @@ def __init__(self): self.__voltage_3v3 = "NaN" self.__current_dc = "NaN" self.__voltage_dc = "NaN" + # + self.__current_mpcie3 = "NaN" + self.__voltage_mpcie3 = "NaN" + self.__current_mpcie5 = "NaN" + self.__voltage_mpcie5 = "NaN" + self.__current_mpcie7 = "NaN" + self.__voltage_mpcie7 = "NaN" + # + self.__humidity = "NaN" + self.__pressure = "NaN" + self.__temperature = "NaN" # ---------------------------------------- def get_altitude(self) -> str: + """ + Get altitude + """ return str(self.__altitude) def get_latitude(self) -> str: + """ + Get latitude + """ return str(self.__latitude) def get_longitude(self) -> str: + """ + Get longitude + """ return str(self.__longitude) def get_gps_time(self) -> str: + """ + Get GPS time + """ return self.__gps_time def get_pdop(self) -> str: + """ + Get pdop + """ return str(self.__pdop) def get_speed(self) -> str: + """ + Get speed + """ return str(self.__speed) def get_climb(self) -> str: + """ + Get climb + """ return str(self.__climb) def get_track(self) -> str: + """ + Get track + """ return str(self.__track) def get_cpu_temp(self): + """ + Get CPU temperature + """ return self.__cpu_temp def get_bat_temp(self): + """ + Get battery temperature + """ return self.__bat_temp def get_tmp100(self): + """ + Get TMP100 temperature + """ return self.__tmp100 def get_wifi_temp(self): + """ + Get wifi temperature + """ return self.__wifi_temp def get_battery_voltage(self): + """ + Get battery voltage + """ return self.__battery_voltage def get_battery_current(self): + """ + Get battery current + """ return self.__battery_current def get_nrf_current(self): + """ + Get NRF current + """ return self.__current_nrf def get_nrf_voltage(self): + """ + Get NRF voltage + """ return self.__voltage_nrf def get_3v3_current(self): + """ + Get 3V3 current + """ return self.__current_3v3 def get_3v3_voltage(self): + """ + Get 3V3 voltage + """ return self.__voltage_3v3 def get_dc_current(self): + """ + Get DC current + """ return self.__current_dc def get_dc_voltage(self): + """ + Get DC voltage + """ return self.__voltage_dc + def get_mpcie3_current(self): + """ + Get mPCIe3 current + """ + return self.__current_mpcie3 + + def get_mpcie3_voltage(self): + """ + Get mPCIe3 voltage + """ + return self.__voltage_mpcie3 + + def get_mpcie5_current(self): + """ + Get mPCIe5 current + """ + return self.__current_mpcie5 + + def get_mpcie5_voltage(self): + """ + Get mPCIe5 voltage + """ + return self.__voltage_mpcie5 + + def get_mpcie7_current(self): + """ + Get mPCIe7 current + """ + return self.__current_mpcie7 + + def get_mpcie7_voltage(self): + """ + Get mPCIe7 voltage + """ + return self.__voltage_mpcie7 + + def get_humidity(self): + """ + Get humidity + """ + return self.__humidity + + def get_pressure(self): + """ + Get pressure + """ + return self.__pressure + + def get_temperature(self): + """ + Get temperature + """ + return self.__temperature + # ---------------------------------------- def __update_battery_status(self): - self.__battery_voltage = read_value("/sys/class/power_supply/max1726x_battery/voltage_now") - self.__battery_current = read_value("/sys/class/power_supply/max1726x_battery/current_now") - - def __get_cpu_load(self) -> str: - load = read_value("/proc/loadavg") - return load.split()[0] + """ + Update battery status + """ + if self.__max17260_connected: + # check if max17260 is connected and if not, set all values to NaN and + # don't try to check anymore ( causing a lot of errors in the dmesg log ) + if read_value("/sys/class/power_supply/max1726x_battery/uevent") == "": + self.__battery_voltage = "NaN" + self.__battery_current = "NaN" + self.__max17260_connected = False + return + + self.__battery_voltage = read_value( + get_hwmon_path("/sys/class/power_supply/max1726x_battery/voltage_now")) + self.__battery_current = read_value( + get_hwmon_path("/sys/class/power_supply/max1726x_battery/current_now")) + + # @staticmethod + # def __get_cpu_load() -> str: + # """ + # Get CPU load + # """ + # load = read_value("/proc/loadavg") + # return load.split()[0] + + def __update_bme280_status(self): + """ + Update BME280 status + """ + self.__humidity = read_value( + get_hwmon_path("/sys/bus/iio/devices/iio:device*/in_humidityrelative_input")) + self.__pressure = read_value( + get_hwmon_path("/sys/bus/iio/devices/iio:device*/in_pressure_input")) + self.__temperature = read_value( + get_hwmon_path("/sys/bus/iio/devices/iio:device*/in_temp_input")) + + def __update_ina231_status(self): + """ + Update INA231 status + """ + if self.__pcb_version == "1": + self.__current_mpcie3 = read_value( + get_hwmon_path("/sys/class/i2c-adapter/i2c-1/1-0046/hwmon/hwmon*/curr1_input")) + self.__voltage_mpcie3 = read_value( + get_hwmon_path("/sys/class/i2c-adapter/i2c-1/1-0046/hwmon/hwmon*/in1_input")) + self.__current_mpcie5 = read_value( + get_hwmon_path("/sys/class/i2c-adapter/i2c-1/1-0045/hwmon/hwmon*/curr1_input")) + self.__voltage_mpcie5 = read_value( + get_hwmon_path("/sys/class/i2c-adapter/i2c-1/1-0045/hwmon/hwmon*/in1_input")) + self.__current_mpcie7 = read_value( + get_hwmon_path("/sys/class/i2c-adapter/i2c-1/1-0040/hwmon/hwmon*/curr1_input")) + self.__voltage_mpcie7 = read_value( + get_hwmon_path("/sys/class/i2c-adapter/i2c-1/1-0040/hwmon/hwmon*/in1_input")) + self.__current_nrf = read_value( + get_hwmon_path("/sys/class/i2c-adapter/i2c-1/1-0044/hwmon/hwmon*/curr1_input")) + self.__voltage_nrf = read_value( + get_hwmon_path("/sys/class/i2c-adapter/i2c-1/1-0044/hwmon/hwmon*/in1_input")) def __update_ina2xx_status(self): - self.__current_nrf = read_value( - get_hwmon_path_from_options( - ["/sys/class/i2c-adapter/i2c-1/1-0040/hwmon/hwmon*/curr1_input", - "/sys/class/i2c-adapter/i2c-10/10-0040/hwmon/hwmon*/curr1_input"])) - self.__voltage_nrf = read_value( - get_hwmon_path_from_options( - ["/sys/class/i2c-adapter/i2c-1/1-0040/hwmon/hwmon*/in1_input", - "/sys/class/i2c-adapter/i2c-10/10-0040/hwmon/hwmon*/in1_input"])) + """ + Update INA2xx status + """ + if self.__pcb_version != "1": + self.__current_nrf = read_value( + get_hwmon_path("/sys/class/i2c-adapter/i2c-1/1-0040/hwmon/hwmon*/curr1_input")) + self.__voltage_nrf = read_value( + get_hwmon_path("/sys/class/i2c-adapter/i2c-1/1-0040/hwmon/hwmon*/in1_input")) - self.__current_3v3 = read_value( - get_hwmon_path_from_options( - ["/sys/class/i2c-adapter/i2c-1/1-0045/hwmon/hwmon*/curr1_input", - "/sys/class/i2c-adapter/i2c-10/10-0045/hwmon/hwmon*/curr1_input"])) - self.__voltage_3v3 = read_value( - get_hwmon_path_from_options( - ["/sys/class/i2c-adapter/i2c-1/1-0045/hwmon/hwmon*/in1_input", - "/sys/class/i2c-adapter/i2c-10/10-0045/hwmon/hwmon*/in1_input"])) + self.__current_3v3 = read_value( + get_hwmon_path("/sys/class/i2c-adapter/i2c-1/1-0045/hwmon/hwmon*/curr1_input")) + self.__voltage_3v3 = read_value( + get_hwmon_path("/sys/class/i2c-adapter/i2c-1/1-0045/hwmon/hwmon*/in1_input")) self.__current_dc = read_value( - get_hwmon_path("/sys/class/i2c-adapter/i2c-0/0-0041/hwmon/hwmon*/curr1_input")) - + get_hwmon_path_from_options( + ["/sys/class/i2c-adapter/i2c-0/0-0041/hwmon/hwmon*/curr1_input", + "/sys/class/i2c-adapter/i2c-1/1-0041/hwmon/hwmon*/curr1_input"])) self.__voltage_dc = read_value( - get_hwmon_path("/sys/class/i2c-adapter/i2c-0/0-0041/hwmon/hwmon*/in1_input")) + get_hwmon_path_from_options( + ["/sys/class/i2c-adapter/i2c-0/0-0041/hwmon/hwmon*/in1_input", + "/sys/class/i2c-adapter/i2c-1/1-0041/hwmon/hwmon*/in1_input"])) def __update_temperatures(self): + """ + Update temperatures + """ self.__cpu_temp = read_value("/sys/class/thermal/thermal_zone0/temp") self.__bat_temp = read_value("/sys/class/thermal/thermal_zone1/temp") self.__tmp100 = read_value( @@ -168,9 +365,12 @@ def __update_temperatures(self): get_hwmon_path("/sys/class/ieee80211/phy0/device/hwmon/hwmon*/temp1_input")) def __update_gpsd_data(self): - if not self.__gpsdConnected: + """ + Update GPSD data + """ + if not self.__gpsd_connected: gpsd.connect() - self.__gpsdConnected = True + self.__gpsd_connected = True try: gps_response = gpsd.get_current() @@ -202,6 +402,8 @@ def update(self): self.__update_temperatures() self.__update_battery_status() self.__update_ina2xx_status() + self.__update_ina231_status() + self.__update_bme280_status() # print(f"lat: {self.latitude}") # print(f"lon: {self.longitude}") # print(f"alt: {self.altitude}") diff --git a/common/tools/field_test_logger/wifi_info.py b/common/tools/field_test_logger/wifi_info.py index 699637be0..dcef943a8 100644 --- a/common/tools/field_test_logger/wifi_info.py +++ b/common/tools/field_test_logger/wifi_info.py @@ -1,12 +1,9 @@ import subprocess import re -EXPECTED_INTERFACE = "wlp1s0" - - class WifiInfo: - def __init__(self, interval): + def __init__(self, interval, interface, batman_interface): self.__neighbors = "" self.__originators = "" self.__channel = "" @@ -20,12 +17,14 @@ def __init__(self, interval): self.__old_rx_bytes = 0 self.__old_tx_bytes = 0 self.__interval_seconds = interval + self.__interface = interface + self.__batman_interface = batman_interface # ---------------------------------------- def get_mac_addr(self): try: - with open(f"/sys/class/net/{EXPECTED_INTERFACE}/address", 'r') as f: + with open(f"/sys/class/net/{self.__interface}/address", 'r') as f: value = f.readline() return value.strip() except: @@ -111,7 +110,7 @@ def __update_channel_and_twpower(self): phyname = line.rstrip() # Correct interface is wlp1s0 - if f"Interface {EXPECTED_INTERFACE}" in line: + if f"Interface {self.__interface}" in line: interface_ok = True # Capture correct phyname for later usage __phyname = phyname.replace('#', '') @@ -131,7 +130,7 @@ def __update_channel_and_twpower(self): self.__txpower = txpower def __update_mcs_and_rssi(self): - iw_cmd = ['iw', 'dev', f"{EXPECTED_INTERFACE}", 'station', 'dump'] + iw_cmd = ['iw', 'dev', f"{self.__interface}", 'station', 'dump'] iw_proc = subprocess.Popen(iw_cmd, stdout=subprocess.PIPE) out = iw_proc.communicate()[0].decode().rstrip() @@ -162,7 +161,7 @@ def __update_mcs_and_rssi(self): self.__stations[station_mac] = [rssi, tx_mcs, rx_mcs] def __update_noise(self): - iw_cmd = ['iw', 'dev', f"{EXPECTED_INTERFACE}", 'survey', 'dump'] + iw_cmd = ['iw', 'dev', f"{self.__interface}", 'survey', 'dump'] iw_proc = subprocess.Popen(iw_cmd, stdout=subprocess.PIPE) out = iw_proc.communicate()[0].decode().rstrip() @@ -216,7 +215,7 @@ def __update_throughputs(self): tx_bytes = 0 for line in lines: - if EXPECTED_INTERFACE in line: + if self.__interface in line: parts = line.split() rx_bytes = int(parts[1]) - self.__old_rx_bytes tx_bytes = int(parts[9]) - self.__old_tx_bytes @@ -232,7 +231,7 @@ def __update_throughputs(self): #print(f"tx throughput: {self.tx_throughput}") def __update_batman_neighbors(self): - batctl_cmd = ['batctl', 'n', '-n', '-H'] + batctl_cmd = ['batctl', 'meshif', self.__batman_interface, 'n', '-n', '-H'] batctl_proc = subprocess.Popen(batctl_cmd, stdout=subprocess.PIPE) @@ -253,7 +252,7 @@ def __update_batman_neighbors(self): #print(self.neighbors) def __update_batman_originators(self): - batctl_cmd = ['batctl', 'o', '-n', '-H'] + batctl_cmd = ['batctl', 'meshif', self.__batman_interface, 'o', '-n', '-H'] batctl_proc = subprocess.Popen(batctl_cmd, stdout=subprocess.PIPE)