Commit 8da02a5b authored by Olivier Kaufmann's avatar Olivier Kaufmann
Browse files

Fixes logging data when in simulation mode

Showing with 118 additions and 117 deletions
+118 -117
...@@ -44,7 +44,6 @@ class MQTTHandler(logging.Handler): ...@@ -44,7 +44,6 @@ class MQTTHandler(logging.Handler):
self.tls = tls self.tls = tls
self.protocol = protocol self.protocol = protocol
self.transport = transport self.transport = transport
print(f'init logger QoS={self.qos}')
def emit(self, record): def emit(self, record):
""" """
......
...@@ -10,7 +10,7 @@ Olivier KAUFMANN (UMONS) and Guillaume BLANCHY (ILVO). ...@@ -10,7 +10,7 @@ Olivier KAUFMANN (UMONS) and Guillaume BLANCHY (ILVO).
import os import os
import io import io
import json import json
import subprocess # import subprocess
import numpy as np import numpy as np
import csv import csv
...@@ -388,110 +388,116 @@ class OhmPi(object): ...@@ -388,110 +388,116 @@ class OhmPi(object):
""" """
# TODO here we can add the current_injected or voltage_injected in mA or mV # TODO here we can add the current_injected or voltage_injected in mA or mV
# check arguments # check arguments
if nb_stack is None:
nb_stack = self.settings['nb_stack']
if injection_duration is None:
injection_duration = self.settings['injection_duration']
start_time = time.time()
# inner variable initialization
injection_current = 0
sum_vmn = 0
sum_ps = 0
# injection courant and measure
pin0 = self.mcp.get_pin(0)
pin0.direction = Direction.OUTPUT
pin1 = self.mcp.get_pin(1)
pin1.direction = Direction.OUTPUT
pin0.value = False
pin1.value = False
self.exec_logger.debug('Starting measurement') self.exec_logger.debug('Starting measurement')
self.exec_logger.info('Waiting for data') self.exec_logger.info('Waiting for data')
# FUNCTION AUTOGAIN if self.on_pi:
# ADS1115 for current measurement (AB) if nb_stack is None:
self.ads_current = ads.ADS1115(self.i2c, gain=2 / 3, data_rate=860, address=0x48) nb_stack = self.settings['nb_stack']
# ADS1115 for voltage measurement (MN) if injection_duration is None:
self.ads_voltage = ads.ADS1115(self.i2c, gain=2 / 3, data_rate=860, address=0x49) injection_duration = self.settings['injection_duration']
# try auto gain
pin1.value = True start_time = time.time()
pin0.value = False
time.sleep(injection_duration) # inner variable initialization
gain_current = self.gain_auto(AnalogIn(self.ads_current, ads.P0)) injection_current = 0
gain_voltage = self.gain_auto(AnalogIn(self.ads_voltage, ads.P0, ads.P1)) sum_vmn = 0
pin0.value = False sum_ps = 0
pin1.value = False
print('gain current: {:.3f}, gain voltage: {:.3f}'.format(gain_current, gain_voltage)) # injection courant and measure
self.ads_current = ads.ADS1115(self.i2c, gain=gain_current, data_rate=860, address=0x48) pin0 = self.mcp.get_pin(0)
self.ads_voltage = ads.ADS1115(self.i2c, gain=gain_voltage, data_rate=860, address=0x49) pin0.direction = Direction.OUTPUT
pin1 = self.mcp.get_pin(1)
# TODO I don't get why 3 + 2*nb_stack - 1? why not just range(nb_stack)? pin1.direction = Direction.OUTPUT
# or do we consider 1 stack = one full polarity? do we discard the first 3 readings? pin0.value = False
for n in range(0, 3 + 2 * nb_stack - 1):
# current injection
if (n % 2) == 0:
pin1.value = True
pin0.value = False # current injection polarity nr1
else:
pin0.value = True
pin1.value = False # current injection nr2
start_delay = time.time() # stating measurement time
time.sleep(injection_duration) # delay depending on current injection duration
# measurement of current i and voltage u
# sampling for each stack at the end of the injection
meas = np.zeros((self.nb_samples, 3))
for k in range(0, self.nb_samples):
# reading current value on ADS channel A0
meas[k, 0] = (AnalogIn(self.ads_current, ads.P0).voltage * 1000) / (50 * self.r_shunt) # TODO: replace 50 by factor depending on INA model specifed in config.py
# reading voltage value on ADS channel A2
meas[k, 1] = -AnalogIn(self.ads_voltage, ads.P0, ads.P1).voltage * self.coef_p2 * 1000 # NOTE: Changed sign
# stop current injection
pin1.value = False pin1.value = False
# FUNCTION AUTOGAIN
# ADS1115 for current measurement (AB)
self.ads_current = ads.ADS1115(self.i2c, gain=2 / 3, data_rate=860, address=0x48)
# ADS1115 for voltage measurement (MN)
self.ads_voltage = ads.ADS1115(self.i2c, gain=2 / 3, data_rate=860, address=0x49)
# try auto gain
pin1.value = True
pin0.value = False pin0.value = False
end_delay = time.time() time.sleep(injection_duration)
gain_current = self.gain_auto(AnalogIn(self.ads_current, ads.P0))
# take average from the samples per stack, then sum them all gain_voltage = self.gain_auto(AnalogIn(self.ads_voltage, ads.P0, ads.P1))
# average for all stack is done outside the loop pin0.value = False
injection_current = injection_current + (np.mean(meas[:, 0])) pin1.value = False
vmn1 = np.mean(meas[:, 1]) - np.mean(meas[:, 2]) print('gain current: {:.3f}, gain voltage: {:.3f}'.format(gain_current, gain_voltage))
if (n % 2) == 0: self.ads_current = ads.ADS1115(self.i2c, gain=gain_current, data_rate=860, address=0x48)
sum_vmn = sum_vmn - vmn1 self.ads_voltage = ads.ADS1115(self.i2c, gain=gain_voltage, data_rate=860, address=0x49)
sum_ps = sum_ps + vmn1
else: # TODO I don't get why 3 + 2*nb_stack - 1? why not just range(nb_stack)?
sum_vmn = sum_vmn + vmn1 # or do we consider 1 stack = one full polarity? do we discard the first 3 readings?
sum_ps = sum_ps + vmn1 for n in range(0, 3 + 2 * nb_stack - 1):
# current injection
# TODO get battery voltage and warn if battery is running low if (n % 2) == 0:
# TODO send a message on SOH stating the battery level pin1.value = True
end_calc = time.time() pin0.value = False # current injection polarity nr1
else:
# TODO I am not sure I understand the computation below pin0.value = True
# wait twice the actual injection time between two injection pin1.value = False # current injection nr2
# so it's a 50% duty cycle right? start_delay = time.time() # stating measurement time
time.sleep(2 * (end_delay - start_delay) - (end_calc - start_delay)) time.sleep(injection_duration) # delay depending on current injection duration
# create a dictionary and compute averaged values from all stacks # measurement of current i and voltage u
d = { # sampling for each stack at the end of the injection
"time": datetime.now().isoformat(), meas = np.zeros((self.nb_samples, 3))
"A": quad[0], for k in range(0, self.nb_samples):
"B": quad[1], # reading current value on ADS channel A0
"M": quad[2], meas[k, 0] = (AnalogIn(self.ads_current, ads.P0).voltage * 1000) / (50 * self.r_shunt) # TODO: replace 50 by factor depending on INA model specifed in config.py
"N": quad[3], # reading voltage value on ADS channel A2
"inj time [ms]": (end_delay - start_delay) * 1000, meas[k, 1] = -AnalogIn(self.ads_voltage, ads.P0, ads.P1).voltage * self.coef_p2 * 1000 # NOTE: Changed sign
"Vmn [mV]": (sum_vmn / (3 + 2 * nb_stack - 1)),
"I [mA]": (injection_current / (3 + 2 * nb_stack - 1)), # stop current injection
"R [ohm]": (sum_vmn / (3 + 2 * nb_stack - 1) / (injection_current / (3 + 2 * nb_stack - 1))), pin1.value = False
"Ps [mV]": (sum_ps / (3 + 2 * nb_stack - 1)), pin0.value = False
"nbStack": nb_stack, end_delay = time.time()
"CPU temp [degC]": CPUTemperature().temperature,
"Time [s]": (-start_time + time.time()), # take average from the samples per stack, then sum them all
"Nb samples [-]": self.nb_samples # average for all stack is done outside the loop
} injection_current = injection_current + (np.mean(meas[:, 0]))
vmn1 = np.mean(meas[:, 1]) - np.mean(meas[:, 2])
if (n % 2) == 0:
sum_vmn = sum_vmn - vmn1
sum_ps = sum_ps + vmn1
else:
sum_vmn = sum_vmn + vmn1
sum_ps = sum_ps + vmn1
# TODO get battery voltage and warn if battery is running low
# TODO send a message on SOH stating the battery level
end_calc = time.time()
# TODO I am not sure I understand the computation below
# wait twice the actual injection time between two injection
# so it's a 50% duty cycle right?
time.sleep(2 * (end_delay - start_delay) - (end_calc - start_delay))
# create a dictionary and compute averaged values from all stacks
d = {
"time": datetime.now().isoformat(),
"A": quad[0],
"B": quad[1],
"M": quad[2],
"N": quad[3],
"inj time [ms]": (end_delay - start_delay) * 1000,
"Vmn [mV]": (sum_vmn / (3 + 2 * nb_stack - 1)),
"I [mA]": (injection_current / (3 + 2 * nb_stack - 1)),
"R [ohm]": (sum_vmn / (3 + 2 * nb_stack - 1) / (injection_current / (3 + 2 * nb_stack - 1))),
"Ps [mV]": (sum_ps / (3 + 2 * nb_stack - 1)),
"nbStack": nb_stack,
"CPU temp [degC]": CPUTemperature().temperature,
"Time [s]": (-start_time + time.time()),
"Nb samples [-]": self.nb_samples
}
else: # for testing, generate random data
d = {'time': datetime.now().isoformat(), 'A': quad[0], 'B': quad[1], 'M': quad[2], 'N': quad[3],
'R [ohm]': np.abs(np.random.randn(1))
}
# round number to two decimal for nicer string output # round number to two decimal for nicer string output
output = [f'{k}\t' for k in d.keys()] output = [f'{k}\t' for k in d.keys()]
...@@ -504,6 +510,7 @@ class OhmPi(object): ...@@ -504,6 +510,7 @@ class OhmPi(object):
output += f'{val}\t' output += f'{val}\t'
output = output[:-1] output = output[:-1]
self.exec_logger.debug(output) self.exec_logger.debug(output)
self.data_logger.info(json.loads(d))
time.sleep(1) # NOTE: why this? time.sleep(1) # NOTE: why this?
return d return d
...@@ -572,14 +579,12 @@ class OhmPi(object): ...@@ -572,14 +579,12 @@ class OhmPi(object):
msg = f'Contact resistance {str(quad):s}: I: {current * 1000.:>10.3f} mA, V: {voltage * 1000.:>10.3f} mV, ' \ msg = f'Contact resistance {str(quad):s}: I: {current * 1000.:>10.3f} mA, V: {voltage * 1000.:>10.3f} mV, ' \
f'R: {resistance /1000.:>10.3f} kOhm' f'R: {resistance /1000.:>10.3f} kOhm'
print(msg)
self.exec_logger.debug(msg) self.exec_logger.debug(msg)
# if contact resistance = 0 -> we have a short circuit!! # if contact resistance = 0 -> we have a short circuit!!
if resistance < 1e-2: if resistance < 1e-2:
msg = f'!!!SHORT CIRCUIT!!! {str(quad):s}: {resistance / 1000.:.3f} kOhm' msg = f'!!!SHORT CIRCUIT!!! {str(quad):s}: {resistance / 1000.:.3f} kOhm'
self.exec_logger.warning(msg) self.exec_logger.warning(msg)
print(msg)
# save data and print in a text file # save data and print in a text file
self.append_and_save(export_path_rs, { self.append_and_save(export_path_rs, {
...@@ -628,16 +633,17 @@ class OhmPi(object): ...@@ -628,16 +633,17 @@ class OhmPi(object):
# last_measurement.to_csv(f, header=True) # last_measurement.to_csv(f, header=True)
def process_commands(self): def process_commands(self):
tcp_port = CONTROL_CONFIG['tcp_port']
context = zmq.Context() context = zmq.Context()
tcp_port = CONTROL_CONFIG["tcp_port"]
socket = context.socket(zmq.REP) socket = context.socket(zmq.REP)
socket.bind(f'tcp://*:{tcp_port}') socket.bind(f'tcp://*:{tcp_port}')
print(colored(f'Listening to commands on tcp port {tcp_port}.' print(colored(f'Listening to commands on tcp port {tcp_port}.'
f' Make sure your client interface is running and bound to this port...', 'blue')) f' Make sure your client interface is running and bound to this port...', 'blue'))
self.exec_logger.debug(f'Start listening for commands on port {tcp_port}') self.exec_logger.debug(f'Start listening for commands on port {tcp_port}')
while True: while True:
message = socket.recv() message = socket.recv()
print(f'Received command: {message}') self.exec_logger.debug(f'Received command: {message}')
e = None e = None
try: try:
cmd_id = None cmd_id = None
...@@ -652,7 +658,8 @@ class OhmPi(object): ...@@ -652,7 +658,8 @@ class OhmPi(object):
self._update_acquisition_settings(args) self._update_acquisition_settings(args)
elif cmd == 'start': elif cmd == 'start':
self.measure(cmd_id) self.measure(cmd_id)
self.stop() while not self.status == 'idle':
time.sleep(0.1)
status = True status = True
elif cmd == 'stop': elif cmd == 'stop':
self.stop() self.stop()
...@@ -683,7 +690,7 @@ class OhmPi(object): ...@@ -683,7 +690,7 @@ class OhmPi(object):
finally: finally:
reply = {'cmd_id': cmd_id, 'status': status} reply = {'cmd_id': cmd_id, 'status': status}
reply = json.dumps(reply) reply = json.dumps(reply)
print(reply) self.exec_logger.debug(f'Execution report: {reply}')
self.exec_logger.debug(reply) self.exec_logger.debug(reply)
reply = bytes(reply, 'utf-8') reply = bytes(reply, 'utf-8')
socket.send(reply) socket.send(reply)
...@@ -722,25 +729,20 @@ class OhmPi(object): ...@@ -722,25 +729,20 @@ class OhmPi(object):
self.switch_mux_on(quad) self.switch_mux_on(quad)
# run a measurement # run a measurement
if self.on_pi: acquired_data = self.run_measurement(quad, self.settings["nb_stack"],
current_measurement = self.run_measurement(quad, self.settings["nb_stack"],
self.settings["injection_duration"]) self.settings["injection_duration"])
else: # for testing, generate random data
current_measurement = {
'A': [quad[0]], 'B': [quad[1]], 'M': [quad[2]], 'N': [quad[3]],
'R [ohm]': np.abs(np.random.randn(1))
}
# switch mux off # switch mux off
self.switch_mux_off(quad) self.switch_mux_off(quad)
# add command_id in dataset # add command_id in dataset
current_measurement.update({'cmd_id': cmd_id}) acquired_data.update({'cmd_id': cmd_id})
# log data to the data logger # log data to the data logger
self.data_logger.info(f'{current_measurement}') self.data_logger.info(f'{acquired_data}')
print(f'{acquired_data}')
# save data and print in a text file # save data and print in a text file
self.append_and_save(filename, current_measurement) self.append_and_save(filename, acquired_data)
self.exec_logger.debug('{:d}/{:d}'.format(i + 1, self.sequence.shape[0])) self.exec_logger.debug(f'{i+1:d}/{self.sequence.shape[0]:d}')
# compute time needed to take measurement and subtract it from interval # compute time needed to take measurement and subtract it from interval
# between two sequence run (= sequence_delay) # between two sequence run (= sequence_delay)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment