Commit dfd280e2 authored by Guillaume Blanchy's avatar Guillaume Blanchy
Browse files

UI update

Showing with 764 additions and 576 deletions
+764 -576
data/*.csv data/*
**/.ipynb_notebooks/** **/.ipynb_notebooks/**
data.zip data.zip
__pycache__ __pycache__
Ohmpi_4elec_mqtt.py Ohmpi_4elec_mqtt.py
ohmpy/*
logs/*
sequence.txt
...@@ -12,7 +12,8 @@ OHMPI_CONFIG = { ...@@ -12,7 +12,8 @@ OHMPI_CONFIG = {
'integer': 2, # Max value 10 # TODO: Explain what this is... 'integer': 2, # Max value 10 # TODO: Explain what this is...
'version': 2, 'version': 2,
'max_elec': 64, 'max_elec': 64,
'board_address': {'A': 0x76, 'B': 0x71, 'M': 0x74, 'N': 0x70} # def. {'A': 0x76, 'B': 0x71, 'M': 0x74, 'N': 0x70} 'board_address': {'A': 0x70, 'B': 0x71, 'M': 0x72, 'N': 0x73} # def. {'A': 0x76, 'B': 0x71, 'M': 0x74, 'N': 0x70}
#'board_address': {'A': 0x76, 'B': 0x71, 'M': 0x74, 'N': 0x70} # def. {'A': 0x76, 'B': 0x71, 'M': 0x74, 'N': 0x70}
} }
# Execution logging configuration # Execution logging configuration
...@@ -48,7 +49,7 @@ SOH_LOGGING_CONFIG = { ...@@ -48,7 +49,7 @@ SOH_LOGGING_CONFIG = {
# MQTT logging configuration parameters # MQTT logging configuration parameters
MQTT_LOGGING_CONFIG = { MQTT_LOGGING_CONFIG = {
'hostname': 'ohmpy.umons.ac.be', 'hostname': 'raspberrypi.local',
'port': 1883, 'port': 1883,
'qos': 0, 'qos': 0,
'retain': False, 'retain': False,
......
In this folder will be logged all measurements done with the OhmPi as .csv.
env.sh 100644 → 100755
#!/bin/bash #!/bin/bash
sudo apt-get install -y libatlas-base-dev sudo apt-get install -y libatlas-base-dev
python3 -m venv ohmpy python3 -m venv ohmpy
source ohmpy/bin/activate || exit 1 # NOTE: Added || exit to avoid installing requirements in system python source ohmpy/bin/activate || exit 1 # NOTE: Added || exit to avoid installing requirements in system python
......
This diff is collapsed.
File mode changed from 100644 to 100755
...@@ -120,7 +120,7 @@ class OhmPi(object): ...@@ -120,7 +120,7 @@ class OhmPi(object):
- injection_duration (in seconds) - injection_duration (in seconds)
- nbr_meas (total number of times the sequence will be run) - nbr_meas (total number of times the sequence will be run)
- sequence_delay (delay in second between each sequence run) - sequence_delay (delay in second between each sequence run)
- stack (number of stack for each quadrupole measurement) - nb_stack (number of stack for each quadrupole measurement)
- export_path (path where to export the data, timestamp will be added to filename) - export_path (path where to export the data, timestamp will be added to filename)
Parameters Parameters
...@@ -375,7 +375,7 @@ class OhmPi(object): ...@@ -375,7 +375,7 @@ 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: if nb_stack is None:
nb_stack = self.pardict['stack'] nb_stack = self.pardict['nb_stack']
if injection_duration is None: if injection_duration is None:
injection_duration = self.pardict['injection_duration'] injection_duration = self.pardict['injection_duration']
...@@ -410,8 +410,7 @@ class OhmPi(object): ...@@ -410,8 +410,7 @@ class OhmPi(object):
gain_voltage = self.gain_auto(AnalogIn(self.ads_voltage, ads.P0, ads.P1)) gain_voltage = self.gain_auto(AnalogIn(self.ads_voltage, ads.P0, ads.P1))
pin0.value = False pin0.value = False
pin1.value = False pin1.value = False
print(gain_current) print('gain current: {:.3f}, gain voltage: {:.3f}'.format(gain_current, gain_voltage))
print(gain_voltage)
self.ads_current = ads.ADS1115(self.i2c, gain=gain_current, data_rate=860, address=0x48) self.ads_current = ads.ADS1115(self.i2c, gain=gain_current, data_rate=860, address=0x48)
self.ads_voltage = ads.ADS1115(self.i2c, gain=gain_voltage, data_rate=860, address=0x49) self.ads_voltage = ads.ADS1115(self.i2c, gain=gain_voltage, data_rate=860, address=0x49)
...@@ -465,20 +464,20 @@ class OhmPi(object): ...@@ -465,20 +464,20 @@ class OhmPi(object):
# create a dictionary and compute averaged values from all stacks # create a dictionary and compute averaged values from all stacks
d = { d = {
"time": [datetime.now().isoformat()], "time": datetime.now().isoformat(),
"A": quad[0], "A": quad[0],
"B": quad[1], "B": quad[1],
"M": quad[2], "M": quad[2],
"N": quad[3], "N": quad[3],
"inj time [ms]": (end_delay - start_delay) * 1000, "inj time [ms]": (end_delay - start_delay) * 1000,
"Vmn [mV]": [(sum_vmn / (3 + 2 * nb_stack - 1))], "Vmn [mV]": (sum_vmn / (3 + 2 * nb_stack - 1)),
"I [mA]": [(injection_current / (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)))], "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))], "Ps [mV]": (sum_ps / (3 + 2 * nb_stack - 1)),
"nbStack": [nb_stack], "nbStack": nb_stack,
"CPU temp [degC]": [CPUTemperature().temperature], "CPU temp [degC]": CPUTemperature().temperature,
"Time [s]": [(-start_time + time.time())], "Time [s]": (-start_time + time.time()),
"Nb samples [-]": [self.nb_samples] "Nb samples [-]": self.nb_samples
} }
# round number to two decimal for nicer string output # round number to two decimal for nicer string output
...@@ -500,18 +499,45 @@ class OhmPi(object): ...@@ -500,18 +499,45 @@ class OhmPi(object):
""" Check contact resistance. """ Check contact resistance.
""" """
# create custom sequence where MN == AB # create custom sequence where MN == AB
nelec = self.sequence.max() # number of elec used in the sequence # we only check the electrodes which are in the sequence (not all might be connected)
elec = np.sort(np.unique(self.sequence.flatten())) # assumed order
quads = np.vstack([ quads = np.vstack([
np.arange(nelec - 1) + 1, elec[:-1],
np.arange(nelec - 1) + 2, elec[1:],
np.arange(nelec - 1) + 1, elec[:-1],
np.arange(nelec - 1) + 2 elec[1:],
]).T ]).T
# create filename to store RS
export_path_rs = self.pardict['export_path'].replace('.csv', '') \
+ '_' + datetime.now().strftime('%Y%m%dT%H%M%S') + '_rs.csv'
# perform RS check
self.run = True
self.status = 'running'
# make sure all mux are off to start with
self.reset_mux()
# measure all quad of the RS sequence
for i in range(0, quads.shape[0]): for i in range(0, quads.shape[0]):
quad = quads[i, :] # quadrupole quad = quads[i, :] # quadrupole
self.reset_mux()
self.switch_mux_on(quad) # NOTE (GB): I'd use the self.run_measurement() for all this middle part so we an make use of autogain and so ...
# call the switch_mux function to switch to the right electrodes
#self.switch_mux_on(quad)
# run a measurement
#current_measurement = self.run_measurement(quad, 1, 0.25)
# switch mux off
#self.switch_mux_off(quad)
# save data and print in a text file
#self.append_and_save(export_path_rs, current_measurement)
# current injection
pin0 = self.mcp.get_pin(0) pin0 = self.mcp.get_pin(0)
pin0.direction = Direction.OUTPUT pin0.direction = Direction.OUTPUT
pin1 = self.mcp.get_pin(1) pin1 = self.mcp.get_pin(1)
...@@ -519,40 +545,51 @@ class OhmPi(object): ...@@ -519,40 +545,51 @@ class OhmPi(object):
pin0.value = False pin0.value = False
pin1.value = False pin1.value = False
print(quad)
# call the switch_mux function to switch to the right electrodes # call the switch_mux function to switch to the right electrodes
self.switch_mux_on(quad)
self.ads_current = ads.ADS1115(self.i2c, gain=2 / 3, data_rate=860, address=0x48) self.ads_current = ads.ADS1115(self.i2c, gain=2 / 3, data_rate=860, address=0x48)
# ADS1115 for voltage measurement (MN) # ADS1115 for voltage measurement (MN)
self.ads_voltage = ads.ADS1115(self.i2c, gain=2 / 3, data_rate=860, address=0x49) self.ads_voltage = ads.ADS1115(self.i2c, gain=2 / 3, data_rate=860, address=0x49)
pin1.value = True pin1.value = True # inject from pin1 to pin0
pin0.value = False pin0.value = False
time.sleep(0.2) time.sleep(0.2)
# measure current and voltage
current = AnalogIn(self.ads_current, ads.P0).voltage / (50 * self.r_shunt) current = AnalogIn(self.ads_current, ads.P0).voltage / (50 * self.r_shunt)
voltage = -AnalogIn(self.ads_voltage, ads.P0, ads.P1).voltage * 2.5 voltage = -AnalogIn(self.ads_voltage, ads.P0, ads.P1).voltage * 2.5
resistance = voltage / current resistance = voltage / current
# print(B)
# print(A) # compute resistance measured (= contact resistance)
print(abs(round(resistance / 1000, 1)), "kOhm") resist = abs(resistance / 1000)
msg = 'Contact resistance {:s}: {:.3f} kOhm'.format(
str(quad), resist)
print(msg)
self.exec_logger.debug(msg)
# if contact resistance = 0 -> we have a short circuit!!
if resist < 1e-5:
msg = '!!!SHORT CIRCUIT!!! {:s}: {:.3f} kOhm'.format(
str(quad), resist)
self.exec_logger.warning(msg)
print(msg)
# save data and print in a text file
self.append_and_save(export_path_rs, {
'A': quad[0],
'B': quad[1],
'RS [kOhm]': resist,
})
# close mux path and put pin back to GND
self.switch_mux_off(quad) self.switch_mux_off(quad)
pin0.value = False pin0.value = False
pin1.value = False pin1.value = False
self.reset_mux()
self.status = 'idle'
self.run = False
# # create backup TODO not good
# export_path = self.pardict['export_path']
# sequence = self.sequence.copy()
#
# # assign new value
# self.pardict['export_path'] = export_path.replace('.csv', '_rs.csv')
# self.sequence = quads
# print(self.sequence)
#
# # run the RS check
# self.log_exec('RS check (check contact resistance)', level='debug')
# self.measure()
#
# # restore
# self.pardict['export_path'] = export_path
# self.sequence = sequence
# #
# # TODO if interrupted, we would need to restore the values # # TODO if interrupted, we would need to restore the values
# # TODO or we offer the possiblity in 'run_measurement' to have rs_check each time? # # TODO or we offer the possiblity in 'run_measurement' to have rs_check each time?
...@@ -616,7 +653,7 @@ class OhmPi(object): ...@@ -616,7 +653,7 @@ class OhmPi(object):
# run a measurement # run a measurement
if self.on_pi: if self.on_pi:
current_measurement = self.run_measurement(quad, self.pardict["stack"], current_measurement = self.run_measurement(quad, self.pardict["nb_stack"],
self.pardict["injection_duration"]) self.pardict["injection_duration"])
else: # for testing, generate random data else: # for testing, generate random data
current_measurement = { current_measurement = {
...@@ -680,7 +717,7 @@ if on_pi: ...@@ -680,7 +717,7 @@ if on_pi:
# and emit a warning otherwise # and emit a warning otherwise
if not arm64_imports: if not arm64_imports:
print(colored(f'Warning: Required packages are missing.\n' print(colored(f'Warning: Required packages are missing.\n'
f'Please run . env.sh at command prompt to update your virtual environment\n', 'yellow')) f'Please run ./env.sh at command prompt to update your virtual environment\n', 'yellow'))
else: else:
print(colored(f'Not running on the Raspberry Pi platform.\nFor simulation purposes only...', 'yellow')) print(colored(f'Not running on the Raspberry Pi platform.\nFor simulation purposes only...', 'yellow'))
......
...@@ -3,6 +3,6 @@ ...@@ -3,6 +3,6 @@
"injection_duration":0.2, "injection_duration":0.2,
"nbr_meas": 100, "nbr_meas": 100,
"sequence_delay": 1, "sequence_delay": 1,
"stack": 1, "nb_stack": 1,
"export_path": "data/measurement.csv" "export_path": "data/measurement.csv"
} }
run.sh 100644 → 100755
source ./ohmpy/bin/activate source ./ohmpy/bin/activate
python3 webserver.py python webserver.py
echo "# start OhmPi web interface" >> $HOME/.bashrc
echo "(cd $HOME/OhmPi; ./run.sh)" >> $HOME/.bashrc
from http.server import SimpleHTTPRequestHandler, HTTPServer from http.server import SimpleHTTPRequestHandler, HTTPServer
# import time import time
import os import os
import json import json
from ohmpi import OhmPi from ohmpi import OhmPi
# import threading import threading
import pandas as pd import pandas as pd
import shutil import shutil
hostName = 'localhost' hostName = "raspberrypi.local" # works for AP-STA
serverPort = 8080 #hostName = "192.168.50.1" # fixed IP in AP-STA mode
#hostName = "0.0.0.0" # for AP mode (not AP-STA)
# https://gist.github.com/MichaelCurrie/19394abc19abd0de4473b595c0e37a3a serverPort = 8080
with open('ohmpi_param.json') as json_file: # https://gist.github.com/MichaelCurrie/19394abc19abd0de4473b595c0e37a3a
pardict = json.load(json_file)
with open('ohmpi_param.json') as json_file:
ohmpi = OhmPi(pardict) pardict = json.load(json_file)
ohmpi = OhmPi(pardict, sequence='breadboard.txt')
class MyServer(SimpleHTTPRequestHandler): #ohmpi = OhmPi(pardict, sequence='dd16s0no8.txt')
# because we use SimpleHTTPRequestHandler, we do not need to implement
# the do_GET() method (if we use the BaseHTTPRequestHandler, we would need to)
class MyServer(SimpleHTTPRequestHandler):
# def do_GET(self): # because we use SimpleHTTPRequestHandler, we do not need to implement
# # normal get for wepages (not so secure!) # the do_GET() method (if we use the BaseHTTPRequestHandler, we would need to)
# print(self.command)
# print(self.headers) # def do_GET(self):
# print(self.request) # # normal get for wepages (not so secure!)
# self.send_response(200) # print(self.command)
# self.send_header("Content-type", "text/html") # print(self.headers)
# self.end_headers() # print(self.request)
# with open(os.path.join('.', self.path[1:]), 'r') as f: # self.send_response(200)
# self.wfile.write(bytes(f.read(), "utf-8")) # self.send_header("Content-type", "text/html")
# self.end_headers()
def do_POST(self): # with open(os.path.join('.', self.path[1:]), 'r') as f:
# global ohmpiThread, status, run # self.wfile.write(bytes(f.read(), "utf-8"))
dic = json.loads(self.rfile.read(int(self.headers['Content-Length']))) def do_POST(self):
rdic = {} global ohmpiThread, status, run
if dic['command'] == 'start': dic = json.loads(self.rfile.read(int(self.headers['Content-Length'])))
ohmpi.measure() rdic = {}
elif dic['command'] == 'stop': if dic['command'] == 'start':
ohmpi.stop() ohmpi.measure()
elif dic['command'] == 'getData': elif dic['command'] == 'stop':
# get all .csv file in data folder ohmpi.stop()
fnames = os.listdir('data/') elif dic['command'] == 'getData':
ddic = {} # get all .csv file in data folder
for fname in fnames: fnames = [fname for fname in os.listdir('data/') if fname[-4:] == '.csv']
if fname.replace('.csv', '') not in dic['surveyNames'] and fname != 'readme.txt': ddic = {}
df = pd.read_csv('data/' + fname) for fname in fnames:
ddic[fname.replace('.csv', '')] = { if (fname.replace('.csv', '') not in dic['surveyNames']
'a': df['A'].tolist(), and fname != 'readme.txt'
'b': df['B'].tolist(), and '_rs' not in fname):
'm': df['M'].tolist(), df = pd.read_csv('data/' + fname)
'n': df['N'].tolist(), ddic[fname.replace('.csv', '')] = {
'rho': df['R [ohm]'].tolist(), 'a': df['A'].tolist(),
} 'b': df['B'].tolist(),
rdic['data'] = ddic 'm': df['M'].tolist(),
elif dic['command'] == 'removeData': 'n': df['N'].tolist(),
shutil.rmtree('data') 'rho': df['R [ohm]'].tolist(),
os.mkdir('data') }
elif dic['command'] == 'setConfig': rdic['data'] = ddic
ohmpi.stop() elif dic['command'] == 'removeData':
cdic = dic['config'] shutil.rmtree('data')
ohmpi.pardict['nb_electrodes'] = int(cdic['nbElectrodes']) os.mkdir('data')
ohmpi.pardict['injection_duration'] = float(cdic['injectionDuration']) elif dic['command'] == 'setConfig':
ohmpi.pardict['nbr_meas'] = int(cdic['nbMeasurements']) ohmpi.stop()
ohmpi.pardict['stack'] = int(cdic['nbStack']) cdic = dic['config']
ohmpi.pardict['sequence_delay'] = int(cdic['sequenceDelay']) ohmpi.pardict['nb_electrodes'] = int(cdic['nbElectrodes'])
print('setConfig', ohmpi.pardict) ohmpi.pardict['injection_duration'] = float(cdic['injectionDuration'])
elif dic['command'] == 'invert': ohmpi.pardict['nbr_meas'] = int(cdic['nbMeasurements'])
pass ohmpi.pardict['nb_stack'] = int(cdic['nbStack'])
elif dic['command'] == 'getResults': ohmpi.pardict['sequence_delay'] = int(cdic['sequenceDelay'])
pass if cdic['sequence'] != '':
elif dic['command'] == 'download': with open('sequence.txt', 'w') as f:
shutil.make_archive('data', 'zip', 'data') f.write(cdic['sequence'])
else: ohmpi.read_quad('sequence.txt')
# command not found print('new sequence set.')
rdic['response'] = 'command not found' print('setConfig', ohmpi.pardict)
elif dic['command'] == 'invert':
rdic['status'] = ohmpi.status pass
self.send_response(200) elif dic['command'] == 'getResults':
self.send_header('Content-Type', 'text/json') pass
self.end_headers() elif dic['command'] == 'rsCheck':
self.wfile.write(bytes(json.dumps(rdic), 'utf8')) ohmpi.rs_check()
fnames = sorted([fname for fname in os.listdir('data/') if fname[-7:] == '_rs.csv'])
df = pd.read_csv('data/' + fnames[-1])
if __name__ == "__main__": ddic = {
webServer = HTTPServer((hostName, serverPort), MyServer) 'AB': (df['A'].astype('str') + '-' + df['B'].astype(str)).tolist(),
print("Server started http://%s:%s" % (hostName, serverPort)) 'res': df['RS [kOhm]'].tolist()
}
try: rdic['data'] = ddic
webServer.serve_forever() elif dic['command'] == 'download':
except KeyboardInterrupt: shutil.make_archive('data', 'zip', 'data')
pass elif dic['command'] == 'shutdown':
print('shutting down...')
webServer.server_close() os.system('shutdown now -h')
print("Server stopped.") elif dic['command'] == 'restart':
print('shutting down...')
os.system('reboot')
else:
# command not found
rdic['response'] = 'command not found'
rdic['status'] = ohmpi.status
self.send_response(200)
self.send_header('Content-Type', 'text/json')
self.end_headers()
self.wfile.write(bytes(json.dumps(rdic), 'utf8'))
if __name__ == "__main__":
webServer = HTTPServer((hostName, serverPort), MyServer)
print("Server started http://%s:%s" % (hostName, serverPort))
try:
webServer.serve_forever()
except KeyboardInterrupt:
pass
webServer.server_close()
print("Server stopped.")
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