diff --git a/configs/config_mb_2024_0_2__4_mux_2024_dps5005.py b/configs/config_mb_2024_0_2__4_mux_2024_dps5005.py index 31c2538e8a5923f963f0f0fbce254307a2be17a4..d36472af02d63b92c01ea4a0854063d617810d42 100644 --- a/configs/config_mb_2024_0_2__4_mux_2024_dps5005.py +++ b/configs/config_mb_2024_0_2__4_mux_2024_dps5005.py @@ -21,9 +21,10 @@ HARDWARE_CONFIG = { 'pwr': {'model': 'pwr_dps5005', 'voltage': 3., 'interface_name': 'modbus'}, 'tx': {'model': 'mb_2024_0_2', 'voltage_max': 50., # Maximum voltage supported by the TX board [V] - 'current_max': 4.8/(50*r_shunt), # Maximum voltage read by the current ADC on the TX board [A] + 'current_max': 4.8/(50*r_shunt)/5, # Maximum voltage read by the current ADC on the TX board [A] 'r_shunt': r_shunt, # Shunt resistance in Ohms - 'interface_name': 'i2c' + 'interface_name': 'i2c', + 'vmn_hardware_offset': 2501. }, 'rx': {'model': 'mb_2024_0_2', 'coef_p2': 1.00, # slope for conversion for ADS, measurement in V/V diff --git a/dev/test_mb_2023_4_mux_2023.py b/dev/test_mb_2023_4_mux_2023.py index 69b292e43b3fc8be7c1d96a9d83dc52c6b5be31e..ca9c9c669028d077929a075488e37c40f928fb04 100644 --- a/dev/test_mb_2023_4_mux_2023.py +++ b/dev/test_mb_2023_4_mux_2023.py @@ -84,10 +84,11 @@ if within_ohmpi: print('Starting test with OhmPi.') k = OhmPi() - k.get_data() - # k.load_sequence(os.path.join(os.path.dirname(__file__), '../sequences/test_circuit_1423.txt')) - # k.reset_mux() - # k.run_sequence(injection_duration=0.2) + # k.get_data() + k.load_sequence(os.path.join(os.path.dirname(__file__), '../sequences/wenner.txt')) + k.reset_mux() + # k.run_multiple_sequences(sequence_delay=20, nb_meas=3) + k.run_sequence(injection_duration=0.2) # k.rs_check(tx_volt=4) # k.test_mux(mux_id=None, activation_time=0.2) # k._hw.switch_mux([A, B, M, N], state='on') diff --git a/dev/test_mb_2024_4_mux_2024.py b/dev/test_mb_2024_4_mux_2024.py index 7d3142d8b4941ab6bef18abe239bb017747fd8ee..d30d4daec881c5fd5640855ec3b948552c6e9260 100644 --- a/dev/test_mb_2024_4_mux_2024.py +++ b/dev/test_mb_2024_4_mux_2024.py @@ -7,9 +7,9 @@ import time import logging from ohmpi.config import HARDWARE_CONFIG -stand_alone = True +stand_alone = False part_of_hardware_system = False -within_ohmpi = False +within_ohmpi = True # Stand alone if stand_alone: @@ -109,14 +109,15 @@ if within_ohmpi: # k._hw.switch_mux([A, B, M, N], state='off') # print(f'OhmPiHardware Resistance: {k._hw.last_rho :.2f} ohm, dev. {k._hw.last_dev:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') # k._hw._plot_readings() - # k.load_sequence('sequences/9991_GRAD_16_s1_a1.txt') - # k.run_sequence(tx_volt=5, injection_duration=1., nb_stack=2, duty_cycle=0.5) + k.load_sequence('sequences/9991_GRAD_16_s1_a1.txt') + k.run_sequence(tx_volt=5, injection_duration=1., nb_stack=2, duty_cycle=0.5) print('using OhmPi') - d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5, tx_volt=5.) + # d = k.run_measurement([A, B, M, N], injection_duration=1., nb_stack=2, duty_cycle=0.5, tx_volt=5., delay=0.01) # print(d) - k._hw._plot_readings() - print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV') - print(f'OhmPi: Resistance: {d["R [Ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') + # k._hw._plot_readings() + # print(f'OhmPiHardware: Resistance: {k._hw.last_resistance() :.2f} ohm, dev. {k._hw.last_dev():.2f} %, sp: {k._hw.sp:.2f} mV, rx bias: {k._hw.rx._bias:.2f} mV') + # print(f'OhmPi: Resistance: {d["R [Ohm]"] :.2f} ohm, dev. {d["R_std [%]"]:.2f} %, rx bias: {k._hw.rx._bias:.2f} mV') # k._hw._plot_readings(save_fig=False) # plot_exec_log('ohmpi/logs/exec.log') change_config('../configs/config_default.py', verbose=False) + diff --git a/index-mqtt.html b/index.html similarity index 84% rename from index-mqtt.html rename to index.html index 69aa13d99bd8a22468805e6ef53fdb85b46ab972..2d8c4eb18c74556192c743c7c11acc60a58d8f20 100755 --- a/index-mqtt.html +++ b/index.html @@ -1,5 +1,8 @@ <!DOCTYPE html> -<!-- debugging instruction to see the messages passing on the RPi +<!-- + To access the interface, + 1. connect to the 'raspi-webgui' wifi with the given password + Open a browserdebugging instruction to see the messages passing on the RPi mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl --> <html> @@ -17,9 +20,10 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl <!-- <script src="js/danfojs/bundle.min.js"></script> --> <!-- <script src="js/mqtt.min.js"></script> --> <script src="js/paho/paho-mqtt.js"></script> + <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> - <div class='container'> + <div class='container-fluid'> <h1>OhmPi Acquisition Board</h1> <!-- nb stacks, on-time --> <button id="update_settingsBtn" type="button" class="btn btn-secondary" data-toggle="modal" data-target="#exampleModal">Settings</button> @@ -39,8 +43,8 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl <!-- Pseudo section --> <select id='surveySelect' class='custom-select'> </select> - <input id="cmin" type="number" value="0"/> - <input id="cmax" type="number" value="150"/> + <input id="cmin" type="number" value=""/> + <input id="cmax" type="number" value=""/> <button id="capplyBtn" type="button" class="btn btn-info">Apply</button> <div id="gd"></div> @@ -64,8 +68,8 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl <select id="surveySelectInv" class='custom-select'> </select> <button id="invertBtn" type="button" class="btn btn-info">Run inversion</button> - <input id="cminInv" type="number" value="0"/> - <input id="cmaxInv" type="number" value="150"/> + <input id="cminInv" type="number" value=""/> + <input id="cmaxInv" type="number" value=""/> <button id="capplyBtnInv" type="button" class="btn btn-info">Apply</button> <div id="inv"></div> @@ -89,33 +93,39 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl <div class="modal-body"> <form> <div class="form-group row"> - <label for="nbElectrodes" class="col-sm-2 col-form-label">Nb electrodes</label> + <label for="nb_electrodes" class="col-sm-2 col-form-label">Nb electrodes</label> <div class="col-sm-10"> - <input type="number" class="form-control-number" id="nbElectrodes" value="64"> + <input type="number" class="form-control-number" id="nb_electrodes" value="64"> </div> </div> <div class="form-group row"> - <label for="injectionDuration" class="col-sm-2 col-form-label">Injection duration [s]</label> + <label for="injection_duration" class="col-sm-2 col-form-label">Injection duration [s]</label> <div class="col-sm-10"> - <input type="number" class="form-control-number" id="injectionDuration" value="0.2"> + <input type="number" class="form-control-number" id="injection_duration" value="0.2"> </div> </div> <div class="form-group row"> - <label for="nbMeasurements" class="col-sm-2 col-form-label">Nb Measurements</label> + <label for="nb_meas" class="col-sm-2 col-form-label">Nb Measurements</label> <div class="col-sm-10"> - <input type="number" class="form-control-number" id="nbMeasurements" value="1"> + <input type="number" class="form-control-number" id="nb_meas" value="1"> </div> </div> <div class="form-group row"> - <label for="sequenceDelay" class="col-sm-2 col-form-label">Sequence delay [s]</label> + <label for="sequence_delay" class="col-sm-2 col-form-label">Sequence delay [s]</label> <div class="col-sm-10"> - <input type="number" class="form-control-number" id="sequenceDelay" value="100"> + <input type="number" class="form-control-number" id="sequence_delay" value="100"> </div> </div> <div class="form-group row"> - <label for="nbStack" class="col-sm-2 col-form-label">Nb stack</label> + <label for="nb_stack" class="col-sm-2 col-form-label">Nb stack</label> <div class="col-sm-10"> - <input type="number" class="form-control-number" id="nbStack" value="1"> + <input type="number" class="form-control-number" id="nb_stack" value="1"> + </div> + </div> + <div class="form-group row"> + <label for="duty_cycle" class="col-sm-2 col-form-label">Duty cycle [0 -> 1]</label> + <div class="col-sm-10"> + <input type="number" class="form-control-number" id="duty_cycle" value="0.5"> </div> </div> <div class="form-group row"> @@ -202,17 +212,32 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl } function onMessageArrived(message) { - console.log("onMessageArrived:" + message.payloadString) try { let payload = message.payloadString if (message.topic == topic_data) { // process data msg = payload // for accessing the variable from the console - console.log('DATA', payload) + console.log('DATA LOG:', payload) let ddic = JSON.parse(payload.split('INFO:')[1]) - // check cmd_id is any - processMessage(ddic) + // RS check data + if ('rsdata' in ddic) { + rsdata[0]['x'].push(ddic['rsdata']['A']), //, + '-' + ddic['rsdata']['B'], + rsdata[0]['y'].push(ddic['rsdata']['rs']), + Plotly.redraw('rs') + + } else if ('download' in ddic) { + let dwl = document.getElementById('download') + dwl.setAttribute('href', serverUrl + '/data.zip') + dwl.setAttribute('download', 'data.zip') + dwl.click() + } else { // data or results from inversion + processMessage(ddic) + } + + if ('status' in ddic) { + document.getElementById('output').value = ddic['status'] + } // usually these don't have a cmd_id so we are not sure when @@ -303,10 +328,13 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl // set configuration function saveConfigBtnFunc() { // collect values from modal - let formVals = {} - for (let field of ['nbElectrodes', 'injectionDuration', - 'nbMeasurements', 'sequenceDelay', 'nbStack']) { - formVals[field] = document.getElementById(field).value + let formVals = { + 'nb_electrodes': parseInt(document.getElementById('nb_electrodes').value), + 'injection_duration': parseFloat(document.getElementById('injection_duration').value), + 'nb_meas': parseInt(document.getElementById('nb_meas').value), + 'sequence_delay': parseFloat(document.getElementById('sequence_delay').value), + 'nb_stack': parseInt(document.getElementById('nb_stack').value), + 'duty_cycle': parseFloat(document.getElementById('duty_cycle').value), } console.log(formVals) @@ -392,7 +420,7 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl } } - Plotly.newPlot('gd', [trace], layout) + Plotly.newPlot('gd', [trace], layout, {responsive: true}) // make time-serie plot let tdata = [] @@ -405,7 +433,7 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl title: 'Sampling time' } } - Plotly.newPlot('ts', tdata, layout2) + Plotly.newPlot('ts', tdata, layout2, {responsive: true}) // add trace to time-serie plot function addTraceBtnFunc() { @@ -417,7 +445,7 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl name: val, type: 'scatter' }) - Plotly.newPlot('ts', tdata, layout2) + Plotly.newPlot('ts', tdata, layout2, {responsive: true}) getData() } let addTraceBtn = document.getElementById('addTraceBtn') @@ -427,13 +455,14 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl function removeTracesBtnFunc() { squads = [] tdata = [] - Plotly.newPlot('ts', tdata, layout2) + Plotly.newPlot('ts', tdata, layout2, {responsive: true}) } let removeTracesBtn = document.getElementById('removeTracesBtn') removeTracesBtn.addEventListener('click', removeTracesBtnFunc) // callback function to draw the plot function surveySelectFunc(el) { + let elecSpacing = parseFloat(document.getElementById('elecSpacing').value) let surveyName = el['target'].value let df = data[surveyName] if (df != undefined) { @@ -448,25 +477,44 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl let ypos = [] let app = [] for (let i = 0; i < a.length; i++) { + let emin = Math.min(...[a[i], b[i], m[i], n[i]]) let emax = Math.max(...[a[i], b[i], m[i], n[i]]) let dist = Math.abs(emax - emin) xpos.push(emin + dist/2) ypos.push(Math.sqrt(2)/2 * dist) - // let ab = (a[i] + b[i])/2 - // let mn = (m[i] + n[i])/2 - // xpos.push(Math.min(ab, mn) + dist/2) - // ypos.push(Math.sqrt(2)/2*dist) let am = Math.abs(a[i] - m[i]) let bm = Math.abs(b[i] - m[i]) let an = Math.abs(a[i] - n[i]) let bn = Math.abs(a[i] - n[i]) let K = (2*Math.PI)/((1/am)-(1/an)-(1/an)+(1/bn)) app.push(df['rho'][i]*K) + + // let a = df['a'][i] + // let b = df['b'][i] + // let m = df['m'][i] + // let n = df['n'][i] + + // // compute geometric factor assuming flat 2D surface + // let am = Math.abs(a - m)*elecSpacing + // let bm = Math.abs(b - m)*elecSpacing + // let an = Math.abs(a - n)*elecSpacing + // let bn = Math.abs(b - n)*elecSpacing + // let K = 2*Math.PI/((1/am)-(1/bm)-(1/an)+(1/bn)) + // app.push(df['rho'][i]*K) + // //console.log(K) // same as resipy for the wenner case + + // // computing pseudo-depth assuming 2D flat array + // // let's sort the electrodes AB are the two left, MN, the two right + // let abmn = [a, b, m, n] + // abmn = abmn.sort((a, b) => a - b) + // let ab = (abmn[0] + abmn[1])/2 + // let mn = (abmn[2] + abmn[3])/2 + // let dist = Math.abs(ab - mn) + // xpos.push((Math.min(ab, mn) + dist/2)*elecSpacing) + // ypos.push((Math.sqrt(2)/2*dist)*elecSpacing) } - console.log('xpos', xpos) - console.log('ypos', ypos) - console.log(app) + console.log('========', app, xpos, ypos) // update the trace and redraw the figure trace['x'] = xpos trace['y'] = ypos @@ -479,7 +527,13 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl let surveySelect = document.getElementById('surveySelect') // bar chart for contact resistance - let rsdata = [] + let rsdata = [{ + 'x': [], + 'y': [], + name: 'RS', + type: 'bar', + } + ] let rslayout = { title: 'Contact resistances', yaxis: { @@ -489,7 +543,7 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl title: 'Consecutive electrodes' } } - Plotly.newPlot('rs', rsdata, rslayout) + Plotly.newPlot('rs', rsdata, rslayout, {responsive: true}) // run RS check function rsBtnFunc() { @@ -509,8 +563,13 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl // clear RS graph function rsClearBtnFunc() { - rsdata = [] - Plotly.newPlot('rs', rsdata, rslayout) + rsdata = [{ + 'x': [], + 'y': [], + name: 'RS', + type: 'bar', + }] + Plotly.newPlot('rs', rsdata, rslayout, {responsive: true}) } let rsClearBtn = document.getElementById('rsClearBtn') rsClearBtn.addEventListener('click', rsClearBtnFunc) @@ -530,7 +589,7 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl //if (('status' in ddic) | ('data' in ddic)) { if (ddic.constructor == Object) { // it's a dictionnary // acquisition related - processData(ddic) + processData(ddic) } else { // inversion related invertedData = ddic @@ -686,7 +745,7 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl } } - Plotly.newPlot('inv', invData, invLayout) + Plotly.newPlot('inv', invData, invLayout, {responsive: true}) var btn = document.getElementById('invertBtn') btn.innerText = 'Run inversion' btn.className = 'btn btn-info' @@ -760,11 +819,8 @@ mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl // download data function downloadBtnFunc() { - sendCommand('{"cmd": "download"}', function(x) { - let dwl = document.getElementById('download') - dwl.setAttribute('href', serverUrl + '/data.zip') - dwl.setAttribute('download', 'data.zip') - dwl.click() + sendCommand('{"cmd": "download_data"}', function(x) { + console.log(x) }) } let downloadBtn = document.getElementById('downloadBtn') diff --git a/index_old.html b/index_old.html new file mode 100644 index 0000000000000000000000000000000000000000..b334d18b47d1f8ab64e32028a15fac4c0b513d7e --- /dev/null +++ b/index_old.html @@ -0,0 +1,704 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf8"/> + <title>OhmPi Acquisition Board</title> + <link rel="shortcut icon" type="image/jpg" href="logo_ohmpi.jpg"/> + + <!-- dependencies (need to be local as no internet in AP mode)--> + <script src="js/plotly-basic-2.8.3.min.js"></script> + <script src="js/jquery-3.4.1.min.js"></script> + <link type="text/css" href="css/bootstrap.min.css" rel="stylesheet"> + <!-- <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> --> + <!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous"> --> + <!-- <script src="js/danfojs/bundle.min.js"></script> --> +</head> +<body> + <div class='container'> + <h1>OhmPi Acquisition Board</h1> + <!-- nb stacks, on-time --> + <button id="update_settingsBtn" type="button" class="btn btn-secondary" data-toggle="modal" data-target="#exampleModal">Settings</button> + <button id='runBtn' type="button" class="btn btn-primary">▶</button> + <button id='stopBtn' type="button" class="btn btn-warning">◼</button> + <!-- upload button for csv which display the table ABMN --> + <button id="removeDataBtn" type="button" class="btn btn-danger">Clear data</button> + <button id="getDataBtn" type="button" class="btn btn-info">Get data</button> + <div class="form-check"> + <input id="dataRetrievalCheck" class="form-check-input" type="checkbox" value=""> + <label class="form-check-label" for="dataRetrievalCheck"> + Automaticaly get data every 1 second + </label> + </div> + <div id='output'>Status: idle</div> + + <!-- Pseudo section --> + <select id='surveySelect' class='custom-select'> + </select> + <input id="cmin" type="number" value=""/> + <input id="cmax" type="number" value=""/> + <button id="capplyBtn" type="button" class="btn btn-info">Apply</button> + <div id="gd"></div> + <div id="hoverinfo" style="margin-left:80px;"></div> + <div class="mb3 row"> + <label for="quadSelect">Quadrupole:</label> + <div class="col-sm-10"> + <select id='quadSelect' class='custom-select'></select> + </div> + </div> + + <!-- trace figure --> + <button id="addTraceBtn" type="button" class="btn btn-info">Add trace</button> + <button id="removeTracesBtn" type="button" class="btn btn-info">Remove all traces</button> + <div id="ts"></div> + + <!-- RS check --> + <button id="rsBtn" type="button" class="btn btn-info">Check contact resistance</button> + <button id="getRsBtn" type="button" class="btn btn-info">Get contact resistance</button> + <button id="rsClearBtn" type="button" class="btn btn-info">Clear plot</button> + <div id="rs"></div> + + <!-- Additional buttons --> + <button id="downloadBtn" type="button" class="btn btn-primary">Download data</button> + <!-- <button id="invertBtn" type="button" class="btn btn-primary">Invert</button> --> + <a id="download"></a> + + <!-- Modal for settings --> + <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="exampleModalLabel">OhmPi settings</h5> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <div class="modal-body"> + <form> + <div class="form-group row"> + <label for="nbElectrodes" class="col-sm-2 col-form-label">Nb electrodes</label> + <div class="col-sm-10"> + <input type="number" class="form-control-number" id="nbElectrodes" value=64> + </div> + </div> + <div class="form-group row"> + <label for="injectionDuration" class="col-sm-2 col-form-label">Injection duration [s]</label> + <div class="col-sm-10"> + <input type="number" class="form-control-number" id="injectionDuration" value=0.2> + </div> + </div> + <div class="form-group row"> + <label for="nbMeasurements" class="col-sm-2 col-form-label">Nb Measurements</label> + <div class="col-sm-10"> + <input type="number" class="form-control-number" id="nbMeasurements" value="1"> + </div> + </div> + <div class="form-group row"> + <label for="sequenceDelay" class="col-sm-2 col-form-label">Sequence delay [s]</label> + <div class="col-sm-10"> + <input type="number" class="form-control-number" id="sequenceDelay" value="100"> + </div> + </div> + <div class="form-group row"> + <label for="nbStack" class="col-sm-2 col-form-label">Nb stack</label> + <div class="col-sm-10"> + <input type="number" class="form-control-number" id="nbStack" value="1"> + </div> + </div> + <div class="form-group row"> + <label for="sequence" class="col-sm-2 col-form-label">Sequence</label> + <div class="col-sm-10"> + <input type="file" class="form-control" id="sequence"> + </div> + </div> + <div class="form-group row"> + <label for="elecSpacing" class="col-sm-2 col-form-label">Electrode spacing [m]</label> + <div class="col-sm-10"> + <input type="number" class="form-control" id="elecSpacing", value="1"> + </div> + </div> + </form> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> + <button id="saveConfigBtn" type="button" data-dismiss="modal" class="btn btn-primary">Save</button> + </div> + </div> + </div> + </div> + <button id="restartBtn" type="button" class="btn btn-danger">Restart</button> + <button id="shutdownBtn" type="button" class="btn btn-danger">Shutdown</button> + <footer>v0.2.0</footer> + </div> + + <script type="text/javascript"> + //let serverUrl = 'http://10.3.141.1:8080' + //let serverUrl = 'http://0.0.0.0:8080' + //let serverUrl = 'http://localhost:8080' + let serverUrl = 'http://' + window.location.host + console.log('serverUrl =', serverUrl) + let output = document.getElementById('output') + let data = {} // hold data of all surveys + let interv = null // hold interval for automatic data retrieval + let quads = [] // available quadrupoles for time-serie figure + let squads = [] // selected quadrupoles for time-serie figure + let elecSpacing = 1 // 1 m + + // useful functions + function sendCommand(query, callback=null) { + // dic in the form: {'cmd': X, ...} as JSON + if (callback == null) { + function callback(x) { + console.log('default callback:', x) + } + } + let xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function() { + if (this.readyState == 4) { + if (xhr.status == 200) { + callback(JSON.parse(xhr.response)) + } + } + } + xhr.open('POST', serverUrl) + xhr.setRequestHeader('Content-Type', 'application/json') + xhr.send(query) + } + + // run button + function runBtnFunc() { + sendCommand('{"cmd": "run_multiple_sequences"}', function(x) { + console.log(x['ohmpi_status']) + if (x['ohmpi_status'] == 'running') { + output.innerHTML = 'Status: measuring...' + } + }) + } + let runBtn = document.getElementById('runBtn') + runBtn.addEventListener('click', runBtnFunc) + + // interrupt button + function stopBtnFunc() { + sendCommand('{"cmd": "interrupt"}', function(x) { + output.innerHTML = 'Status: ' + x['ohmpi_status'] + clearInterval(interv) + getData() + }) + } + let stopBtn = document.getElementById('stopBtn') + stopBtn.addEventListener('click', stopBtnFunc) + + // set configuration + function saveSettingsBtnFunc() { + // collect values from modal + let formVals = {} + formVals['nb_electrodes'] = parseInt(document.getElementById('nbElectrodes').value) + formVals['injection_duration'] = parseFloat(document.getElementById('injectionDuration').value) + formVals['nb_meas'] = parseInt(document.getElementById('nbMeasurements').value) + formVals['sequence_delay'] = parseInt(document.getElementById('sequenceDelay').value) + formVals['nb_stack'] = parseInt(document.getElementById('nbStack').value) + formVals['elec_spacing'] = parseFloat(document.getElementById('elecSpacing').value) + console.log(formVals) + elecSpacing = formVals['elec_spacing'] + + // define callback to send settigs to Pi + function settingsCallback() { + sendCommand(JSON.stringify({ + 'cmd': 'update_settings', + 'config': formVals + }), function(x) { + console.log('update_settings', x) + }) + } + + // deal with the potential file containing the sequence + // https://stackoverflow.com/questions/19038919/is-it-possible-to-upload-a-text-file-to-input-in-html-js + if (!window.FileReader) { + alert('Your browser is not supported'); + return false; + } + let input = document.getElementById('sequence') + if (input.files.length) { + const reader = new FileReader() + reader.readAsText(input.files[0]) + reader.addEventListener('load', () => { + formVals['sequence'] = reader.result + console.log('file==', reader.result) + settingsCallback() + }, false) + } else { + console.log('no sequence uploaded') + formVals['sequence'] = '' + settingsCallback() + } + + + } + let saveConfigBtn = document.getElementById('saveConfigBtn') + saveConfigBtn.addEventListener('click', saveSettingsBtnFunc) + + // make pseudo plot + var trace = {} + let layout = {} + let tdata = [] + let layout2 = {} + let rsdata = [] + let rslayout = {} + + // initialize all plots + function initPlots() { + trace = { + x: [], + y: [], + mode: 'markers', + marker: { + size: 40, + color: [], + colorbar: { + title: 'App. res. [Ohm.m]', + cmin: 0, + cmax: 100, + } + } + } + layout = { + title: 'Pseudo-section', + yaxis: { + title: 'Pseudo-depth', + autorange: 'reversed' + }, + xaxis: { + title: 'X' + } + + } + Plotly.newPlot('gd', [trace], layout) + + // make time-serie plot + tdata = [] + layout2 = { + title: 'Time-serie', + yaxis: { + title: 'App. res. [Ohm.m]' + }, + xaxis: { + title: 'Sampling time' + } + } + Plotly.newPlot('ts', tdata, layout2) + + // bar chart for contact resistance + rsdata = [] + rslayout = { + title: 'Contact resistances', + yaxis: { + title: 'Resistance [kOhm]' + }, + xaxis: { + title: 'Consecutive electrodes' + } + } + Plotly.newPlot('rs', rsdata, rslayout) + } + initPlots() + + // hover function + var hoverInfo = document.getElementById('hoverinfo') + document.getElementById('gd').on('plotly_hover', function(data){ + var infotext = data.points.map(function(d){ + return (Math.round(d.data.marker.color[d.pointIndex], 2) + ' Ohm.m'); + }); + hoverInfo.innerHTML = infotext.join('<br/>'); + }) + .on('plotly_unhover', function(data){ + hoverInfo.innerHTML = ''; + }); + + // add trace to time-serie plot + function addTraceBtnFunc() { + let val = document.getElementById('quadSelect').value + squads.push(val.split(', ')) + tdata.push({ + x: [], + y: [], + name: val, + type: 'scatter' + }) + Plotly.newPlot('ts', tdata, layout2) + getData() + } + let addTraceBtn = document.getElementById('addTraceBtn') + addTraceBtn.addEventListener('click', addTraceBtnFunc) + + // remove all traces from time-serie plot + function removeTracesBtnFunc() { + squads = [] + tdata = [] + Plotly.newPlot('ts', tdata, layout2) + } + let removeTracesBtn = document.getElementById('removeTracesBtn') + removeTracesBtn.addEventListener('click', removeTracesBtnFunc) + + // callback function to draw the plot + function surveySelectFunc(el) { + let surveyName = el['target'].value + let df = data[surveyName] + if (df != undefined) { + // let's assume electrodes are 1 m distance + // compute pseudo-depth (assume no topo) + // compute app res (assumping flat, line survey) + let xpos = [] + let ypos = [] + let app = [] + for (let i = 0; i < df['a'].length; i++) { + let a = df['a'][i] + let b = df['b'][i] + let m = df['m'][i] + let n = df['n'][i] + + // compute geometric factor assuming flat 2D surface + let am = Math.abs(a - m)*elecSpacing + let bm = Math.abs(b - m)*elecSpacing + let an = Math.abs(a - n)*elecSpacing + let bn = Math.abs(b - n)*elecSpacing + let K = 2*Math.PI/((1/am)-(1/bm)-(1/an)+(1/bn)) + app.push(df['rho'][i]*K) + //console.log(K) // same as resipy for the wenner case + + // computing pseudo-depth assuming 2D flat array + // let's sort the electrodes AB are the two left, MN, the two right + let abmn = [a, b, m, n] + abmn = abmn.sort((a, b) => a - b) + let ab = (abmn[0] + abmn[1])/2 + let mn = (abmn[2] + abmn[3])/2 + let dist = Math.abs(ab - mn) + xpos.push((Math.min(ab, mn) + dist/2)*elecSpacing) + ypos.push((Math.sqrt(2)/2*dist)*elecSpacing) + + /* + lookupDict = dict(zip(self.elec['label'], np.arange(self.elec.shape[0]))) + array = self.df[['a','b','m','n']].replace(lookupDict).values.astype(int) + elecm = self.elec[['x','y','z']].values.astype(float).copy() # electrode matrix - should be array of floats so np.inf work properly + + ### first determine if measurements are nested ### + #find mid points of AB + AB = (elecm[array[:,0]] + elecm[array[:,1]]) / 2 # mid points of AB + MN = (elecm[array[:,2]] + elecm[array[:,3]]) / 2 # mid points of MN + ABrad = np.sqrt(np.sum((elecm[array[:,0]] - AB)**2,axis=1)) # radius of AB circle + MNrad = np.sqrt(np.sum((elecm[array[:,2]] - MN)**2,axis=1)) # radius of MN circle + + Amn = np.sqrt(np.sum((elecm[array[:,0]] - MN)**2,axis=1)) # distance of A to mid point of MN + Bmn = np.sqrt(np.sum((elecm[array[:,1]] - MN)**2,axis=1)) # distance of B to mid point of MN + Nab = np.sqrt(np.sum((elecm[array[:,2]] - AB)**2,axis=1)) # distance of N to mid point of AB + Mab = np.sqrt(np.sum((elecm[array[:,3]] - AB)**2,axis=1)) # distance of M to mid point of AB + + iABinMN = (Amn < MNrad) & (Bmn < MNrad) + iMNinAB = (Nab < ABrad) & (Mab < ABrad) + inested = iABinMN | iMNinAB #if AB encompasses MN or MN encompasses AB + + # so it will never be taken as minimium + elecm[self.elec['remote'].values,:] = np.inf + + # compute midpoint position of AB and MN dipoles + elecx = elecm[:,0] + elecy = elecm[:,1] + + #CURRENT ELECTRODE MIDPOINTS + caddx = np.abs(elecx[array[:,0]]-elecx[array[:,1]])/2 + caddy = np.abs(elecy[array[:,0]]-elecy[array[:,1]])/2 + caddx[np.isinf(caddx)] = 0 + caddy[np.isinf(caddy)] = 0 + cmiddlex = np.min([elecx[array[:,0]], elecx[array[:,1]]], axis=0) + caddx + cmiddley = np.min([elecy[array[:,0]], elecy[array[:,1]]], axis=0) + caddy + + #POTENTIAL ELECTRODE MIDPOINTS + paddx = np.abs(elecx[array[:,2]]-elecx[array[:,3]])/2 + paddy = np.abs(elecy[array[:,2]]-elecy[array[:,3]])/2 + paddx[np.isinf(paddx)] = 0 + paddy[np.isinf(paddy)] = 0 + pmiddlex = np.min([elecx[array[:,2]], elecx[array[:,3]]], axis=0) + paddx + pmiddley = np.min([elecy[array[:,2]], elecy[array[:,3]]], axis=0) + paddy + + + # for non-nested measurements + xposNonNested = np.min([cmiddlex, pmiddlex], axis=0) + np.abs(cmiddlex-pmiddlex)/2 + yposNonNested = np.min([cmiddley, pmiddley], axis=0) + np.abs(cmiddley-pmiddley)/2 + pcdist = np.sqrt((cmiddlex-pmiddlex)**2 + (cmiddley-pmiddley)**2) + + # zposNonNested = np.sqrt(2)/2*pcdist + zposNonNested = pcdist/4 + + if np.all(cmiddley-pmiddley == 0): + zposNonNested = 0.25*pcdist + else: # for 3D arrays where there are mid-line measurements, this works closer to inversion results + zposNonNested = np.sqrt(2)/2*pcdist + + # for nested measurements use formula of Dalhin 2006 + xposNested = np.zeros(len(pmiddlex)) + yposNested = np.zeros(len(pmiddlex)) + outerElec1 = np.zeros((len(pmiddlex), 2)) # position of one electrode of outer dipole + outerElec2 = np.zeros((len(pmiddlex), 2)) # position of one electrode of outer dipole + # innerMid = np.zeros((len(pmiddlex), 2)) # middle of inner dipole + if np.sum(iMNinAB) > 0: + xposNested[iMNinAB] = pmiddlex[iMNinAB] + yposNested[iMNinAB] = pmiddley[iMNinAB] + outerElec1[iMNinAB] = np.c_[elecx[array[iMNinAB,0]], elecy[array[iMNinAB,0]]] + outerElec2[iMNinAB] = np.c_[elecx[array[iMNinAB,1]], elecy[array[iMNinAB,1]]] + + if np.sum(iABinMN) > 0: + xposNested[iABinMN] = cmiddlex[iABinMN] + yposNested[iABinMN] = cmiddley[iABinMN] + outerElec1[iABinMN] = np.c_[elecx[array[iABinMN,2]], elecy[array[iABinMN,2]]] + outerElec2[iABinMN] = np.c_[elecx[array[iABinMN,3]], elecy[array[iABinMN,3]]] + + innerMid = np.c_[pmiddlex, pmiddley] # always use potential dipole + + apdist = np.sqrt(np.sum((outerElec1-innerMid)**2, axis=1)) + bpdist = np.sqrt(np.sum((outerElec2-innerMid)**2, axis=1)) + zposNested = np.min([apdist, bpdist], axis=0)/3 + + xpos = np.zeros_like(pmiddlex) + ypos = np.zeros_like(pmiddlex) + zpos = np.zeros_like(pmiddlex) + + xpos[~inested] = xposNonNested[~inested] + xpos[inested] = xposNested[inested] + + ypos[~inested] = yposNonNested[~inested] + ypos[inested] = yposNested[inested] + + zpos[~inested] = zposNonNested[~inested] + zpos[inested] = zposNested[inested] + + */ + } + //console.log(app) + // update the trace and redraw the figure + trace['x'] = xpos + trace['y'] = ypos + trace['marker']['color'] = app + trace['marker']['cmax'] = document.getElementById('cmax').value + trace['marker']['cmin'] = document.getElementById('cmin').value + Plotly.redraw('gd') + } + } + let surveySelect = document.getElementById('surveySelect') + + // run RS check + function rsBtnFunc() { + sendCommand('{"cmd": "rsCheck"}', function (a) {}) + } + let rsBtn = document.getElementById('rsBtn') + rsBtn.addEventListener('click', rsBtnFunc) + + // get RS check data + function getRsBtnFunc() { + sendCommand('{"cmd": "getRsCheck"}', function(res) { + // update the bar plot + rsdata.push({ + x: res['data']['AB'], + y: res['data']['res'], + name: 'RS', + type: 'bar' + }) + Plotly.redraw('rs') + }) + } + let getRsBtn = document.getElementById('getRsBtn') + getRsBtn.addEventListener('click', getRsBtnFunc) + + // clear RS graph + function rsClearBtnFunc() { + rsdata = [] + Plotly.newPlot('rs', rsdata, rslayout) + } + let rsClearBtn = document.getElementById('rsClearBtn') + rsClearBtn.addEventListener('click', rsClearBtnFunc) + + // getData + function getData() { + let surveyNames = [] + sendCommand(JSON.stringify({ + 'cmd': 'getData', + 'surveyNames': surveyNames + // last survey is often partial so we download it again + }), function(ddic) { + // update status + //output.innerHTML = 'Status: ' + ddic['status'] + + // update data dic with new data + data = { // destructuring assignement (magic! :o) + ...data, + ...ddic['data'] // value from second dic are preferred + } + + // dropdown with number of surveys + surveyNames = Object.keys(data).sort() + + // remove listener as we will replace the choices + surveySelect.removeEventListener('change', surveySelectFunc) + surveySelect.innerHTML = '' // clearing all child nodes + + // add choices again + for (let surveyName of surveyNames) { + let option = document.createElement('option') + option.innerText = surveyName + option.value = surveyName + surveySelect.appendChild(option) + } + + // listener again + surveySelect.addEventListener('change', surveySelectFunc) + + // plot last one by default + surveySelect.value = surveyNames[surveyNames.length - 1] + + // call the function directly + // (as progammatically chaging the value does not trigger the event) + surveySelectFunc({'target': surveySelect}) + + // update list of quadrupoles if any + let idiff = false + if (data[surveyNames[0]] != undefined) { + idiff = quads.length != data[surveyNames[0]]['a'].length + } + //console.log('idiff=', idiff, quads.length, data[surveyNames[0]]['a'].length) + if (((quads.length == 0) | idiff) & (data[surveyNames[0]] != undefined)){ + console.log('updating list of quadrupoles') + quads = [] + let df = data[surveyNames[0]] + let quadSelect = document.getElementById('quadSelect') + quadSelect.innerHTML = '' + for (let i = 0; i < df['a'].length; i++) { + quad = [df['a'][i], df['b'][i], df['m'][i], df['n'][i]] + quads.push(quad) + let option = document.createElement('option') + option.value = quad.join(', ') + option.innerText = quad.join(', ') + quadSelect.appendChild(option) + } + console.log('quads=', quads) + } + + // update time-serie figure + if (squads.length > 0) { + + // transform all surveyNames to datetime + let xt = [] + for (surveyName of surveyNames) { + let a = surveyName.split('_').slice(-1)[0] + xt.push(a.slice(0, 4) + '-' + + a.slice(4, 6) + '-' + + a.slice(6, 8) + ' ' + + a.slice(9, 11) + ':' + + a.slice(11, 13) + ':' + + a.slice(13, 15)) + } + //console.log(xt) + + // create one new trace per selected quadrupole + for (let k = 0; k < squads.length; k++) { + squad = squads[k] + let x = [] + let y = [] + for (let i = 0; i < surveyNames.length; i++) { + df = data[surveyNames[i]] + for (let j = 0; j < df['a'].length; j++) { + if (df['a'][j] == squad[0] + && df['b'][j] == squad[1] + && df['m'][j] == squad[2] + && df['n'][j] == squad[3]) { + y.push(df['rho'][j]) + x.push(xt[i]) + break + } + } + } + + // update trace dictionnary + tdata[k]['x'] = x + tdata[k]['y'] = y + } + //console.log(tdata) + Plotly.redraw('ts') + } + }) + } + let getDataBtn = document.getElementById('getDataBtn') + getDataBtn.addEventListener('click', getData) + + // apply new colorscale + let capplyBtn = document.getElementById('capplyBtn') + capplyBtn.addEventListener('click', function() { + surveySelectFunc({'target': surveySelect}) + }) + + // checkbox interaction for data download + function dataRetrievalCheckFunc(x) { + if (x['target'].checked == true) { + interv = setInterval(getData, 1000) // every 5s + } else { + clearInterval(interv) + } + } + let dataRetrievalCheck = document.getElementById('dataRetrievalCheck') + dataRetrievalCheck.addEventListener('change', dataRetrievalCheckFunc) + + // remove data + function removeDataBtnFunc() { + sendCommand('{"cmd": "removeData"}',function(x) { + data = {} + output.innerHTML = 'Status: ' + x['ohmpi_status'] + ' (all data cleared)' + console.log('all data removed') + initPlots() // reset all plots + }) + } + let removeDataBtn = document.getElementById('removeDataBtn') + removeDataBtn.addEventListener('click', removeDataBtnFunc) + + // shutdown Pi + function shutdownBtnFunc() { + sendCommand('{"cmd": "shutdown"}', function(x) { + console.log('shuting down...') + }) + } + let shutdownBtn = document.getElementById('shutdownBtn') + shutdownBtn.addEventListener('click', shutdownBtnFunc) + + // restart Pi + function restartBtnFunc() { + sendCommand('{"cmd": "restart"}', function(x) { + console.log('rebooting...') + }) + } + let restartBtn = document.getElementById('restartBtn') + restartBtn.addEventListener('click', restartBtnFunc) + + // invert data + // function invertBtnFunc() { + // sendCommand('{"cmd": "invert"}', function(x) { + // console.log('inversion results', x) + // }) + // } + // let invertBtn = document.getElementById('invertBtn') + // invertBtn.addEventListener('click', invertBtnFunc) + + // download data + function downloadBtnFunc() { + sendCommand('{"cmd": "download"}', function(x) { + let dwl = document.getElementById('download') + dwl.setAttribute('href', serverUrl + '/data.zip') + dwl.setAttribute('download', 'data.zip') + dwl.click() + }) + } + let downloadBtn = document.getElementById('downloadBtn') + downloadBtn.addEventListener('click', downloadBtnFunc) + + + </script> + + <!-- Boostrap scripts (at the end of the page for faster loading time)--> + <script src="js/bootstrap.bundle.min.js"></script> + <!-- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js" integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script> --> +</body> +</html> diff --git a/ohmpi/config.py b/ohmpi/config.py index fa30ccaadc748d016cd2307bff9678217e04259d..40665972ec78fcbb165cf92d2b3b5fb2fe617836 100644 --- a/ohmpi/config.py +++ b/ohmpi/config.py @@ -17,14 +17,15 @@ OHMPI_CONFIG = { 'settings': 'ohmpi_settings.json', # INSERT YOUR FAVORITE SETTINGS FILE HERE } +r_shunt = 2. HARDWARE_CONFIG = { 'ctl': {'model': 'raspberry_pi'}, 'pwr': {'model': 'pwr_batt', 'voltage': 12., 'interface_name': 'none'}, 'tx': {'model': 'mb_2023_0_X', - 'voltage_max': 12., # Maximum voltage supported by the TX board [V] - 'adc_voltage_max': 4800., # Maximum voltage read by the current ADC on the TX board [mA] - 'r_shunt': 2., # Shunt resistance in Ohms - 'interface_name': 'i2c', + 'voltage_max': 50., # Maximum voltage supported by the TX board [V] + 'current_max': 4.80/(50*r_shunt), # Maximum voltage read by the current ADC on the TX board [A] + 'r_shunt': r_shunt, # Shunt resistance in Ohms + 'interface_name': 'i2c' }, 'rx': {'model': 'mb_2023_0_X', 'coef_p2': 2.50, # slope for conversion for ADS, measurement in V/V diff --git a/ohmpi/hardware_components/abstract_hardware_components.py b/ohmpi/hardware_components/abstract_hardware_components.py index be1e51e2c2712e249988d975d014503f8b1eacc7..5b98a620f4f1873104c14a6a3c6f4cc84ef6e7cb 100644 --- a/ohmpi/hardware_components/abstract_hardware_components.py +++ b/ohmpi/hardware_components/abstract_hardware_components.py @@ -423,7 +423,7 @@ class TxAbstract(ABC): def pwr_state(self, state): if state == 'on': self._pwr_state = 'on' - if not self.pwr.switchable: + if not self.pwr._switchable: self.exec_logger.debug(f'{self.model} cannot switch on power source') self.pwr.reload_settings() elif state == 'off': diff --git a/ohmpi/hardware_components/mb_2023_0_X.py b/ohmpi/hardware_components/mb_2023_0_X.py index f9ae9368f2ec997d3fdf598b97a4644993e061de..7ba2a1652aa1d34523076bb589523293f70863d1 100644 --- a/ohmpi/hardware_components/mb_2023_0_X.py +++ b/ohmpi/hardware_components/mb_2023_0_X.py @@ -21,6 +21,7 @@ SPECS = {'rx': {'model': {'default': os.path.basename(__file__).rstrip('.py')}, 'mcp_address': {'default': None}, 'ads_address': {'default': 0x49}, 'voltage_min': {'default': 10.0}, + 'voltage_max': {'default': 5000.0}, # [mV] 'vmn_hardware_offset': {'default': 0.} }, 'tx': {'model': {'default': os.path.basename(__file__).rstrip('.py')}, diff --git a/ohmpi/hardware_components/mb_2024_0_2.py b/ohmpi/hardware_components/mb_2024_0_2.py index 33fe7ee461234b44c7a711558b96b85f5dc0742f..99f3883315f9116f9c26e67f8c7ad99e49b8afb9 100644 --- a/ohmpi/hardware_components/mb_2024_0_2.py +++ b/ohmpi/hardware_components/mb_2024_0_2.py @@ -21,6 +21,7 @@ SPECS = {'rx': {'model': {'default': os.path.basename(__file__).rstrip('.py')}, 'mcp_address': {'default': 0x27}, 'ads_address': {'default': 0x49}, 'voltage_min': {'default': 10.0}, + 'voltage_max': {'default': 5000.0}, # [mV] 'dg411_gain_ratio': {'default': 1/2}, # lowest resistor value over sum of resistor values 'vmn_hardware_offset': {'default': 2500.}, }, diff --git a/ohmpi/hardware_components/pwr_dps5005.py b/ohmpi/hardware_components/pwr_dps5005.py index 13fed777042690c38fc74104e46bbbd54028014f..f6279ed22ca678fdc4fedd0a3f69504610a1b2ad 100644 --- a/ohmpi/hardware_components/pwr_dps5005.py +++ b/ohmpi/hardware_components/pwr_dps5005.py @@ -71,8 +71,8 @@ class Pwr(PwrAbstract): self._battery_voltage = self.connection.read_register(0x05, 2) return self._battery_voltage - def current_max(self, value): - self.connection.write_register(0x0001, value * 10, 0) + def current_max(self, value): # [mA] + self.connection.write_register(0x0001, int(value*1000), 0) @property def pwr_state(self): @@ -89,6 +89,8 @@ class Pwr(PwrAbstract): """ if state == 'on': self.connection.write_register(0x09, 1) + self.current_max(self._current_max) + print(self._current_max) self._pwr_state = 'on' self.exec_logger.debug(f'{self.model} is on') time.sleep(self._pwr_latency) # from pwr specs diff --git a/ohmpi/hardware_system.py b/ohmpi/hardware_system.py index 19645f09fb90927234a39a0dfcaa2ba11604c587..e91e58e3b89a88486864e9c072b2f3d114eff5ff 100644 --- a/ohmpi/hardware_system.py +++ b/ohmpi/hardware_system.py @@ -43,12 +43,14 @@ for k, v in rx_module.SPECS['rx'].items(): except Exception as e: print(f'Cannot set value {v} in RX_CONFIG[{k}]:\n{e}') -current_max = np.min([TX_CONFIG['current_max'], # TODO: replace 50 by a TX config +current_max = np.min([TX_CONFIG['current_max'], HARDWARE_CONFIG['pwr'].pop('current_max', np.inf), # TODO: replace 50 by a TX config np.min(np.hstack((np.inf, [MUX_CONFIG[i].pop('current_max', np.inf) for i in MUX_CONFIG.keys()])))]) voltage_max = np.min([TX_CONFIG['voltage_max'], np.min(np.hstack((np.inf, [MUX_CONFIG[i].pop('voltage_max', np.inf) for i in MUX_CONFIG.keys()])))]) voltage_min = RX_CONFIG['voltage_min'] +print(f'Current max: {current_max:.2f}') + def elapsed_seconds(start_time): lap = datetime.datetime.utcnow() - start_time @@ -99,6 +101,7 @@ class OhmPiHardware: HARDWARE_CONFIG['pwr'].pop('model') HARDWARE_CONFIG['pwr'].update(**HARDWARE_CONFIG['pwr']) # NOTE: Explain why this is needed or delete me HARDWARE_CONFIG['pwr'].update({'ctl': HARDWARE_CONFIG['pwr'].pop('ctl', self.ctl)}) + HARDWARE_CONFIG['pwr'].update({'current_max': current_max}) if isinstance(HARDWARE_CONFIG['pwr']['ctl'], dict): ctl_mod = HARDWARE_CONFIG['pwr']['ctl'].pop('model', self.ctl) if isinstance(ctl_mod, str): @@ -135,6 +138,7 @@ class OhmPiHardware: if isinstance(self.tx, dict): self.tx = tx_module.Tx(**self.tx) self.tx.pwr = self.pwr + self.tx.pwr._current_max = current_max # Initialize Muxes self._cabling = kwargs.pop('cabling', {}) @@ -355,7 +359,8 @@ class OhmPiHardware: return new_vab def _compute_tx_volt(self, pulse_duration=0.1, strategy='vmax', tx_volt=5., vab_max=voltage_max, - iab_max=current_max, vmn_max=5., vmn_min=voltage_min, polarities=(1, -1), delay=0.050): + iab_max=current_max, vmn_max=None, vmn_min=voltage_min, polarities=(1, -1), delay=0.05, + p_max=None, diff_vab_lim=2.5, n_steps=4): # TODO: Optimise how to pass iab_max, vab_max, vmn_min """Estimates best Tx voltage based on different strategies. At first a half-cycle is made for a short duration with a fixed @@ -397,12 +402,11 @@ class OhmPiHardware: """ if self.tx.pwr.voltage_adjustable: - # Get those values from components - p_max = vab_max * iab_max - - # define a sill - diff_vab_lim = 2.5 - n_steps = 4 + if vmn_max is None: + vmn_max = self.rx._voltage_max / 1000. + print(f'Vmn max: {vmn_max}') + if p_max is None: + p_max = vab_max * iab_max # Set gain at min self.rx.reset_gain() @@ -449,9 +453,9 @@ class OhmPiHardware: if self.tx.pwr.voltage_adjustable: self.tx.voltage = vab_list[k] vab_opt = vab_list[k] - print(f'Selected Vab: {vab_opt:.2f}') - if switch_pwr_off: - self.tx.pwr.pwr_state = 'off' + # print(f'Selected Vab: {vab_opt:.2f}') + # if switch_pwr_off: + # self.tx.pwr.pwr_state = 'off' else: vab_opt = tx_volt # if strategy == 'vmax': diff --git a/ohmpi/http_interface.py b/ohmpi/http_interface.py index 7fd04e37d9e8ae509f56a8e85ea043bd6b22ac73..2b7e6a6534495a6757ba03890fa4220f512cde8a 100644 --- a/ohmpi/http_interface.py +++ b/ohmpi/http_interface.py @@ -2,77 +2,74 @@ from http.server import SimpleHTTPRequestHandler, HTTPServer import os import json import uuid -from config import MQTT_CONTROL_CONFIG, OHMPI_CONFIG -from termcolor import colored -import pandas as pd +# from config import MQTT_CONTROL_CONFIG, OHMPI_CONFIG +# from termcolor import colored +# import pandas as pd import shutil import time -import numpy as np +# import numpy as np from io import StringIO import threading -import paho.mqtt.client as mqtt_client -import paho.mqtt.publish as publish +# import paho.mqtt.client as mqtt_client +# import paho.mqtt.publish as publish hostName = "0.0.0.0" # for AP mode (not AP-STA) -serverPort = 8080 +serverPort = 8000 # https://gist.github.com/MichaelCurrie/19394abc19abd0de4473b595c0e37a3a -ctrl_broker = MQTT_CONTROL_CONFIG['hostname'] -publisher_config = MQTT_CONTROL_CONFIG.copy() -publisher_config['topic'] = MQTT_CONTROL_CONFIG['ctrl_topic'] -publisher_config.pop('ctrl_topic') - -print(colored(f"Sending commands control topic {MQTT_CONTROL_CONFIG['ctrl_topic']} on {MQTT_CONTROL_CONFIG['hostname']} broker.")) -cmd_id = None -received = False -rdic = {} - - -# set controller globally as __init__ seem to be called for each request and so we subscribe again each time (=overhead) -controller = mqtt_client.Client(f"ohmpi_{OHMPI_CONFIG['id']}_interface_http", clean_session=False) # create new instance -print(colored(f"Connecting to control topic {MQTT_CONTROL_CONFIG['ctrl_topic']} on {MQTT_CONTROL_CONFIG['hostname']} broker", 'blue')) -trials = 0 -trials_max = 10 -broker_connected = False -while trials < trials_max: - try: - controller.username_pw_set(MQTT_CONTROL_CONFIG['auth'].get('username'), - MQTT_CONTROL_CONFIG['auth']['password']) - controller.connect(MQTT_CONTROL_CONFIG['hostname']) - trials = trials_max - broker_connected = True - except Exception as e: - print(f'Unable to connect control broker: {e}') - print('trying again to connect to control broker...') - time.sleep(2) - trials += 1 -if broker_connected: - print(f"Subscribing to control topic {MQTT_CONTROL_CONFIG['ctrl_topic']}") - controller.subscribe(MQTT_CONTROL_CONFIG['ctrl_topic'], MQTT_CONTROL_CONFIG['qos']) -else: - print(f"Unable to connect to control broker on {MQTT_CONTROL_CONFIG['hostname']}") - controller = None - - -# start a listener for acknowledgement -def _control(): - def on_message(client, userdata, message): - global cmd_id, rdic, received - - command = json.loads(message.payload.decode('utf-8')) - #print('++++', cmd_id, received, command) - if ('reply' in command.keys()) and (command['cmd_id'] == cmd_id): - print(f'Acknowledgement reception of command {command} by OhmPi') - # print('oooooooooook', command['reply']) - received = True - #rdic = command - - controller.on_message = on_message - controller.loop_forever() +# ctrl_broker = MQTT_CONTROL_CONFIG['hostname'] +# publisher_config = MQTT_CONTROL_CONFIG.copy() +# publisher_config['topic'] = MQTT_CONTROL_CONFIG['ctrl_topic'] +# publisher_config.pop('ctrl_topic') + +# print(colored(f"Sending commands control topic {MQTT_CONTROL_CONFIG['ctrl_topic']} on {MQTT_CONTROL_CONFIG['hostname']} broker.")) +# cmd_id = None +# received = False +# rdic = {} + + +# # set controller globally as __init__ seem to be called for each request and so we subscribe again each time (=overhead) +# controller = mqtt_client.Client(f"ohmpi_{OHMPI_CONFIG['id']}_interface_http", clean_session=False) # create new instance +# print(colored(f"Connecting to control topic {MQTT_CONTROL_CONFIG['ctrl_topic']} on {MQTT_CONTROL_CONFIG['hostname']} broker", 'blue')) +# trials = 0 +# trials_max = 10 +# broker_connected = False +# while trials < trials_max: +# try: +# controller.username_pw_set(MQTT_CONTROL_CONFIG['auth'].get('username'), +# MQTT_CONTROL_CONFIG['auth']['password']) +# controller.connect(MQTT_CONTROL_CONFIG['hostname']) +# trials = trials_max +# broker_connected = True +# except Exception as e: +# print(f'Unable to connect control broker: {e}') +# print('trying again to connect to control broker...') +# time.sleep(2) +# trials += 1 +# if broker_connected: +# print(f"Subscribing to control topic {MQTT_CONTROL_CONFIG['ctrl_topic']}") +# controller.subscribe(MQTT_CONTROL_CONFIG['ctrl_topic'], MQTT_CONTROL_CONFIG['qos']) +# else: +# print(f"Unable to connect to control broker on {MQTT_CONTROL_CONFIG['hostname']}") +# controller = None + + +# # start a listener for acknowledgement +# def _control(): +# def on_message(client, userdata, message): +# global cmd_id, rdic, received + +# command = json.loads(message.payload.decode('utf-8')) +# if ('reply' in command.keys()) and (command['cmd_id'] == cmd_id): +# print(f'Acknowledgement reception of command {command} by OhmPi') +# received = True + +# controller.on_message = on_message +# controller.loop_forever() -t = threading.Thread(target=_control) -t.start() +# t = threading.Thread(target=_control) +# t.start() class MyServer(SimpleHTTPRequestHandler): @@ -124,64 +121,64 @@ class MyServer(SimpleHTTPRequestHandler): cmd_id = uuid.uuid4().hex dic = json.loads(self.rfile.read(int(self.headers['Content-Length']))) rdic = {} # response dictionary - if dic['cmd'] == 'run_multiple_sequences': - payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'run_multiple_sequences'}) - publish.single(payload=payload, **publisher_config) - elif dic['cmd'] == 'interrupt': - payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'interrupt'}) - publish.single(payload=payload, **publisher_config) - elif dic['cmd'] == 'getData': - # get all .csv file in data folder - fnames = [fname for fname in os.listdir('data/') if fname[-4:] == '.csv'] - ddic = {} - for fname in fnames: - if ((fname != 'readme.txt') - and ('_rs' not in fname) - and (fname.replace('.csv', '') not in dic['surveyNames'])): - df = pd.read_csv('data/' + fname) - ddic[fname.replace('.csv', '')] = { - 'a': df['A'].tolist(), - 'b': df['B'].tolist(), - 'm': df['M'].tolist(), - 'n': df['N'].tolist(), - 'rho': df['R [ohm]'].tolist(), - } - rdic['data'] = ddic - elif dic['cmd'] == 'removeData': - shutil.rmtree('data') - os.mkdir('data') - elif dic['cmd'] == 'update_settings': - if 'sequence' in dic['config'].keys() and dic['config']['sequence'] is not None: - sequence = dic['config'].pop('sequence', None) - sequence = np.loadtxt(StringIO(sequence)).astype(int).tolist() # list of list - # we pass the sequence as a list of list as this object is easier to parse for the json.loads() - # of ohmpi._process_commands() - payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'set_sequence', 'kwargs': {'sequence': sequence}}) - print('payload ===', payload) - publish.single(payload=payload, **publisher_config) - payload = json.dumps({'cmd_id': cmd_id + '_settings', 'cmd': 'update_settings', 'kwargs': {'config': dic['config']}}) - cdic = dic['config'] - publish.single(payload=payload, **publisher_config) - elif dic['cmd'] == 'invert': - pass - elif dic['cmd'] == 'getResults': - pass - elif dic['cmd'] == 'rsCheck': - payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'rs_check'}) - publish.single(payload=payload, **publisher_config) - - elif dic['cmd'] == 'getRsCheck': - fnames = sorted([fname for fname in os.listdir('data/') if fname[-7:] == '_rs.csv']) - if len(fnames) > 0: - df = pd.read_csv('data/' + fnames[-1]) - ddic = { - 'AB': (df['A'].astype('str') + '-' + df['B'].astype(str)).tolist(), - 'res': df['RS [kOhm]'].tolist() - } - else: - ddic = {} - rdic['data'] = ddic - elif dic['cmd'] == 'download': + # if dic['cmd'] == 'run_multiple_sequences': + # payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'run_multiple_sequences'}) + # publish.single(payload=payload, **publisher_config) + # elif dic['cmd'] == 'interrupt': + # payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'interrupt'}) + # publish.single(payload=payload, **publisher_config) + # elif dic['cmd'] == 'getData': + # # get all .csv file in data folder + # fnames = [fname for fname in os.listdir('data/') if fname[-4:] == '.csv'] + # ddic = {} + # for fname in fnames: + # if ((fname != 'readme.txt') + # and ('_rs' not in fname) + # and (fname.replace('.csv', '') not in dic['surveyNames'])): + # df = pd.read_csv('data/' + fname) + # ddic[fname.replace('.csv', '')] = { + # 'a': df['A'].tolist(), + # 'b': df['B'].tolist(), + # 'm': df['M'].tolist(), + # 'n': df['N'].tolist(), + # 'rho': df['R [ohm]'].tolist(), + # } + # rdic['data'] = ddic + # elif dic['cmd'] == 'removeData': + # shutil.rmtree('data') + # os.mkdir('data') + # elif dic['cmd'] == 'update_settings': + # if 'sequence' in dic['config'].keys() and dic['config']['sequence'] is not None: + # sequence = dic['config'].pop('sequence', None) + # sequence = np.loadtxt(StringIO(sequence)).astype(int).tolist() # list of list + # # we pass the sequence as a list of list as this object is easier to parse for the json.loads() + # # of ohmpi._process_commands() + # payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'set_sequence', 'kwargs': {'sequence': sequence}}) + # print('payload ===', payload) + # publish.single(payload=payload, **publisher_config) + # payload = json.dumps({'cmd_id': cmd_id + '_settings', 'cmd': 'update_settings', 'kwargs': {'config': dic['config']}}) + # cdic = dic['config'] + # publish.single(payload=payload, **publisher_config) + # elif dic['cmd'] == 'invert': + # pass + # elif dic['cmd'] == 'getResults': + # pass + # elif dic['cmd'] == 'rsCheck': + # payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'rs_check'}) + # publish.single(payload=payload, **publisher_config) + + # elif dic['cmd'] == 'getRsCheck': + # fnames = sorted([fname for fname in os.listdir('data/') if fname[-7:] == '_rs.csv']) + # if len(fnames) > 0: + # df = pd.read_csv('data/' + fnames[-1]) + # ddic = { + # 'AB': (df['A'].astype('str') + '-' + df['B'].astype(str)).tolist(), + # 'res': df['RS [kOhm]'].tolist() + # } + # else: + # ddic = {} + # rdic['data'] = ddic + if dic['cmd'] == 'download': shutil.make_archive('data', 'zip', 'data') elif dic['cmd'] == 'shutdown': print('shutting down...') diff --git a/ohmpi/ohmpi.py b/ohmpi/ohmpi.py index 0fe6a95ccc635aa9aa5ae598b49897521aa62d22..ac661493c6b6ee1276b3e98babd26f1775bc8991 100644 --- a/ohmpi/ohmpi.py +++ b/ohmpi/ohmpi.py @@ -15,7 +15,7 @@ from copy import deepcopy import numpy as np import csv import time -from shutil import rmtree +from shutil import rmtree, make_archive from threading import Thread from inspect import getmembers, isfunction from datetime import datetime @@ -38,7 +38,7 @@ except Exception as error: print(colored(f'Unexpected error: {error}', 'red')) arm64_imports = None -VERSION = '3.0.0-alpha' +VERSION = '3.0.0-beta' class OhmPi(object): @@ -273,10 +273,6 @@ class OhmPi(object): if header == 'R [ohm]': headers[i] = 'R [Ohm]' icols = list(np.where(np.in1d(headers, ['A', 'B', 'M', 'N', 'R [Ohm]']))[0]) - print(headers) - print('+++++', icols) - print(np.array(headers)[np.array(icols)]) - print(np.loadtxt(os.path.join(ddir, fname), delimiter=',', skiprows=1, usecols=icols).shape) data = np.loadtxt(os.path.join(ddir, fname), delimiter=',', skiprows=1, usecols=icols) data = data[None, :] if len(data.shape) == 1 else data @@ -307,6 +303,7 @@ class OhmPi(object): self.exec_logger.debug('Interrupted sequence acquisition...') else: self.exec_logger.debug('No sequence measurement thread to interrupt.') + self.status = 'idle' self.exec_logger.debug(f'Status: {self.status}') def load_sequence(self, filename: str, cmd_id=None): @@ -353,7 +350,7 @@ class OhmPi(object): message : str message containing a command and arguments or keywords and arguments """ - status = False + self.status = 'idle' cmd_id = '?' try: decoded_message = json.loads(message) @@ -374,12 +371,10 @@ class OhmPi(object): except Exception as e: self.exec_logger.error( f"Unable to execute {cmd}({str(kwargs) if kwargs is not None else ''}): {e}") - status = False except Exception as e: self.exec_logger.warning(f'Unable to decode command {message}: {e}') - status = False finally: - reply = {'cmd_id': cmd_id, 'status': status} + reply = {'cmd_id': cmd_id, 'status': self.status} reply = json.dumps(reply) self.exec_logger.debug(f'Execution report: {reply}') @@ -440,9 +435,31 @@ class OhmPi(object): else: self.exec_logger.warning('Not on Raspberry Pi, skipping reboot...') + def download_data(self, cmd_id=None): + """Create a zip of the data folder. + """ + datadir = os.path.join(os.path.dirname(__file__), '../data/') + make_archive(datadir, 'zip', 'data') + self.data_logger.info(json.dumps({'download': 'ready'})) + + def shutdown(self, cmd_id=None): + """Shutdown the Raspberry Pi + + Parameters + ---------- + cmd_id : str, optional + Unique command identifier + """ + + if self.on_pi: + self.exec_logger.info(f'Restarting pi following command {cmd_id}...') + os.system('poweroff') + else: + self.exec_logger.warning('Not on Raspberry Pi, skipping shutdown...') + def run_measurement(self, quad=None, nb_stack=None, injection_duration=None, duty_cycle=None, autogain=True, strategy='constant', tx_volt=5., best_tx_injtime=0.1, - cmd_id=None, **kwargs): + cmd_id=None, vmn_max=None, **kwargs): # TODO: add sampling_interval -> impact on _hw.rx.sampling_rate (store the current value, change the _hw.rx.sampling_rate, do the measurement, reset the sampling_rate to the previous value) # TODO: default value of tx_volt and other parameters set to None should be given in config.py and used in function definition # TODO: add rs_check option (or propose an other way to do this) @@ -500,7 +517,7 @@ class OhmPi(object): bypass_check = kwargs['bypass_check'] if 'bypass_check' in kwargs.keys() else False d = {} if self.switch_mux_on(quad, bypass_check=bypass_check, cmd_id=cmd_id): - tx_volt = self._hw._compute_tx_volt(tx_volt=.5, strategy=strategy, vab_max=10.) # TODO: use tx_volt and vmn_max instead of hardcoded values + tx_volt = self._hw._compute_tx_volt(tx_volt=tx_volt, strategy=strategy, vmn_max=vmn_max) # TODO: use tx_volt and vmn_max instead of hardcoded values self._hw.vab_square_wave(tx_volt, cycle_duration=injection_duration*2/duty_cycle, cycles=nb_stack, duty_cycle=duty_cycle) if 'delay' in kwargs.keys(): delay = kwargs['delay'] @@ -559,6 +576,9 @@ class OhmPi(object): dd['cmd_id'] = str(cmd_id) self.data_logger.info(dd) + self._hw.switch_mux(electrodes=quad[0:2], roles=['A', 'B'], state='on') + time.sleep(0.2) + self._hw.switch_mux(electrodes=quad[0:2], roles=['A', 'B'], state='off') else: self.exec_logger.info(f'Skipping {quad}') @@ -596,6 +616,12 @@ class OhmPi(object): self.exec_logger.debug(f'Status: {self.status}') self.exec_logger.debug(f'Measuring sequence: {self.sequence}') + # # kill previous running thread + # if self.thread is not None: + # self.exec_logger.info('Removing previous thread') + # self.thread.stop() + # self.thread.join() + def func(): for g in range(0, nb_meas): # for time-lapse monitoring if self.status == 'stopping': @@ -603,12 +629,13 @@ class OhmPi(object): break t0 = time.time() self.run_sequence(**kwargs) - # sleeping time between sequence dt = sequence_delay - (time.time() - t0) if dt < 0: dt = 0 if nb_meas > 1: + if self.status == 'stopping': + break time.sleep(dt) # waiting for next measurement (time-lapse) self.status = 'idle' @@ -707,9 +734,11 @@ class OhmPi(object): # save data and print in a text file self.append_and_save(filename, acquired_data) self.exec_logger.debug(f'quadrupole {i + 1:d}/{n:d}') - self._hw.pwr_state = 'off' - self.status = 'idle' + + # reset to idle if we didn't interrupt the sequence + if self.status != 'stopping': + self.status = 'idle' def run_sequence_async(self, cmd_id=None, **kwargs): """Runs the sequence in a separate thread. Can be stopped by 'OhmPi.interrupt()'. @@ -733,7 +762,9 @@ class OhmPi(object): # -> might be a problem at B (cf what we did with WofE) def rs_check(self, tx_volt=5., cmd_id=None): # TODO: add a default value for rs-check in config.py import it in ohmpi.py and add it in rs_check definition - """Checks contact resistances + """Checks contact resistances. + Strategy: we just open A and B, measure the current and using vAB set or + assumed (12V assumed for battery), we compute Rab. Parameters ---------- @@ -748,7 +779,7 @@ class OhmPi(object): # create custom sequence where MN == AB # we only check the electrodes which are in the sequence (not all might be connected) if self.sequence is None: - quads = np.array([[1, 2, 1, 2]], dtype=np.uint32) + quads = np.array([[1, 2, 0, 0]], dtype=np.uint32) else: elec = np.sort(np.unique(self.sequence.flatten())) # assumed order quads = np.vstack([ @@ -773,27 +804,40 @@ class OhmPi(object): # measure all quad of the RS sequence for i in range(0, quads.shape[0]): quad = quads[i, :] # quadrupole - self.switch_mux_on(quad, bypass_check=True) # put before raising the pins (otherwise conflict i2c) - d = self.run_measurement(quad=quad, nb_stack=1, injection_duration=0.2, tx_volt=tx_volt, autogain=False, - bypass_check=True) + self._hw.switch_mux(electrodes=list(quads[i, :2]), roles=['A', 'B'], state='on') + self._hw._vab_pulse(duration=0.2) + current = self._hw.readings[-1, 3] + voltage = self._hw.tx.pwr.voltage * 1000 + time.sleep(0.2) + + # self.switch_mux_on(quad, bypass_check=True) # put before raising the pins (otherwise conflict i2c) + # d = self.run_measurement(quad=quad, nb_stack=1, injection_duration=0.2, tx_volt=tx_volt, autogain=False, + # bypass_check=True) # if self._hw.tx.voltage_adjustable: # voltage = self._hw.tx.voltage # imposed voltage on dps # else: # voltage = self._hw.rx.voltage - voltage = self._hw.rx.voltage - current = self._hw.tx.current + # voltage = self._hw.rx.voltage + # current = self._hw.tx.current # compute resistance measured (= contact resistance) - resist = abs(voltage / current) / 1000. + resist = abs(voltage / current) / 1000 # kOhm # print(str(quad) + '> I: {:>10.3f} mA, V: {:>10.3f} mV, R: {:>10.3f} kOhm'.format( # current, voltage, resist)) - msg = f'Contact resistance {str(quad):s}: I: {current * 1000.:>10.3f} mA, ' \ - f'V: {voltage :>10.3f} mV, ' \ - f'R: {resist :>10.3f} kOhm' - - self.exec_logger.info(msg) + # msg = f'Contact resistance {str(quad):s}: I: {current :>10.3f} mA, ' \ + # f'V: {voltage :>10.3f} mV, ' \ + # f'R: {resist :>10.3f} kOhm' + # create a message as dictionnary to be used by the html interface + msg = { + 'rsdata': { + 'A': int(quad[0]), + 'B': int(quad[1]), + 'rs': resist, # in kOhm + } + } + self.data_logger.info(json.dumps(msg)) # if contact resistance = 0 -> we have a short circuit!! if resist < 1e-5: @@ -829,10 +873,8 @@ class OhmPi(object): try: self.sequence = np.array(sequence).astype(int) # self.sequence = np.loadtxt(StringIO(sequence)).astype('uint32') - status = True except Exception as e: self.exec_logger.warning(f'Unable to set sequence: {e}') - status = False def switch_mux_on(self, quadrupole, bypass_check=False, cmd_id=None): """Switches on multiplexer relays for given quadrupole. @@ -844,7 +886,7 @@ class OhmPi(object): quadrupole : list of 4 int List of 4 integers representing the electrode numbers. bypass_check: bool, optional - Bypasses checks for A==M or A==M or B==M or B==N (i.e. used for rs-check) + Bypasses checks for A==M or A==N or B==M or B==N (i.e. used for rs-check) """ assert len(quadrupole) == 4 if (self._hw.tx.pwr.voltage > self._hw.rx._voltage_max) and bypass_check: @@ -921,7 +963,6 @@ class OhmPi(object): cmd_id : str, optional Unique command identifier """ - status = False if settings is not None: try: if isinstance(settings, dict): @@ -933,10 +974,10 @@ class OhmPi(object): dic = json.load(json_file) self.settings.update(dic) self.exec_logger.debug('Acquisition parameters updated: ' + str(self.settings)) - status = True + self.status = 'idle (acquisition updated)' except Exception as e: # noqa self.exec_logger.warning('Unable to update settings.') - status = False + self.status = 'idle (unable to update settings)' else: self.exec_logger.warning('Settings are missing...') @@ -946,8 +987,6 @@ class OhmPi(object): self.settings['export_dir'] = os.path.split(self.settings['export_path'])[0] self.settings['export_name'] = os.path.split(self.settings['export_path'])[1] - return status - def run_inversion(self, survey_names=[], elec_spacing=1, **kwargs): """Run a simple 2D inversion using ResIPy. diff --git a/ohmpi_settings.json b/ohmpi_settings.json index e839f5fb9a4ddef894a5a8b714757b71cd860c02..2964e24b850157b3fe3a449a4cf5e62a701f82f3 100644 --- a/ohmpi_settings.json +++ b/ohmpi_settings.json @@ -1,5 +1,5 @@ { - "nb_electrodes": 64, + "nb_electrodes": 16, "injection_duration": 0.2, "nb_stack": 1, "nb_meas": 1, diff --git a/run_http_interface.sh b/run_http_interface.sh index 956067d067460056cf3d8ac18b6d519bc7413dfe..f98f082f9b176adf1a1a0afa429146ab9fb33c16 100755 --- a/run_http_interface.sh +++ b/run_http_interface.sh @@ -1,7 +1,7 @@ #!bin/bash -USER="pi" # change if other username +export PYTHONPATH=/home/$USER/OhmPi cd /home/$USER/OhmPi source /home/$USER/OhmPi/ohmpy/bin/activate -python ohmpi.py & # run ohmpi.py to capture the commands -python http_interface.py # run http_interface to serve the web GUI +python dev/start_mqtt_html.py & # run ohmpi.py to capture the commands +python3 -m http.server # run web GUI diff --git a/sequences/test_circuit_1423.txt b/sequences/test_circuit_1423.txt index 691d10eff3a18e8e8d92ec28d9a5aec2753f7d18..397bb332d915a5785e0f5b4f884ba5deaa584623 100644 --- a/sequences/test_circuit_1423.txt +++ b/sequences/test_circuit_1423.txt @@ -2,12 +2,4 @@ 2 5 3 4 3 6 4 5 4 7 5 6 -5 8 6 7 -6 9 7 8 -7 10 8 9 -8 11 9 10 -9 12 10 11 -10 13 11 12 -11 14 12 13 -12 15 13 14 -13 16 14 15 +