diff --git a/.gitignore b/.gitignore index d660b3ade6bb036d44fd6f4b723765a414a1d6a3..411f65872b6b5acccde4445192ad665579f627b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ -data/*.csv +data/* **/.ipynb_notebooks/** data.zip __pycache__ Ohmpi_4elec_mqtt.py +ohmpy/* +logs/* +sequence.txt diff --git a/config.py b/config.py index 90f74494c0c70744633c63180351c8f01e4ddadc..7062f15cf4bc8965b06dfdb1b81d03637d456b07 100644 --- a/config.py +++ b/config.py @@ -12,7 +12,8 @@ OHMPI_CONFIG = { 'integer': 2, # Max value 10 # TODO: Explain what this is... 'version': 2, '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 @@ -48,7 +49,7 @@ SOH_LOGGING_CONFIG = { # MQTT logging configuration parameters MQTT_LOGGING_CONFIG = { - 'hostname': 'ohmpy.umons.ac.be', + 'hostname': 'raspberrypi.local', 'port': 1883, 'qos': 0, 'retain': False, diff --git a/data/readme.txt b/data/readme.txt deleted file mode 100644 index f311a0eec0aa8ba6ccee55358087481c1b89005c..0000000000000000000000000000000000000000 --- a/data/readme.txt +++ /dev/null @@ -1 +0,0 @@ -In this folder will be logged all measurements done with the OhmPi as .csv. diff --git a/env.sh b/env.sh old mode 100644 new mode 100755 index e79f68dfd58fdfd2e7d0a984708ef57c33471693..7f8f33f924942ce32cff08982c81987785e28184 --- a/env.sh +++ b/env.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash sudo apt-get install -y libatlas-base-dev python3 -m venv ohmpy source ohmpy/bin/activate || exit 1 # NOTE: Added || exit to avoid installing requirements in system python diff --git a/index.html b/index.html index 0207a38f5c4978460192d6331f14d2da250f1781..423e5591797f17d7fd9895a803029bce00cddc1d 100644 --- a/index.html +++ b/index.html @@ -1,427 +1,549 @@ -<!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="setConfigBtn" type="button" class="btn btn-secondary" data-toggle="modal" data-target="#exampleModal">Settings</button> - <button id='startBtn' type="button" class="btn btn-primary">Start</button> - <button id='stopBtn' type="button" class="btn btn-warning">Stop</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"> - Automatically get data every 1 second - </label> - </div> - <div id='output'>Status: idle</div> - <select id='surveySelect' class='custom-select'> - </select> - <div id="gd"></div> - <div class="mb3 row"> - <label for="quadSelect">Quadrupole:</label> - <div class="col-sm-10"> - <select id='quadSelect' class='custom-select'></select> - </div> - </div> - <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> - <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 configuration --> - <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 configuration</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> - </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> - - <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 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-series figure - let squads = [] // selected quadrupoles for time-series figure - - // useful functions - function sendCommand(query, callback=null) { - // dic in the form: {'command': 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) - } - - // start button - function startBtnFunc() { - sendCommand('{"command": "start"}', function(x) { - console.log(x['status']) - if (x['status'] == 'running') { - output.innerHTML = 'Status: measuring...' - } - }) - } - let startBtn = document.getElementById('startBtn') - startBtn.addEventListener('click', startBtnFunc) - - // stop button - function stopBtnFunc() { - sendCommand('{"command": "stop"}', function(x) { - output.innerHTML = 'Status: ' + x['status'] - clearInterval(interv) - getData() - }) - } - let stopBtn = document.getElementById('stopBtn') - stopBtn.addEventListener('click', stopBtnFunc) - - // 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 - } - console.log(formVals) - - sendCommand(JSON.stringify({ - 'command': 'setConfig', - 'config': formVals - }), function(x) { - console.log('setconfig:', x) - }) - } - let saveConfigBtn = document.getElementById('saveConfigBtn') - saveConfigBtn.addEventListener('click', saveConfigBtnFunc) - - // make pseudo plot - var trace = { - x: [], - y: [], - mode: 'markers', - marker: { - size: 40, - color: [], - colorbar: { - title: 'App. res. [Ohm.m]' - } - } - } - let layout = { - title: 'Pseudo-section', - yaxis: { - title: 'Pseudo-depth', - autorange: 'reversed' - }, - xaxis: { - title: 'X' - } - - } - Plotly.newPlot('gd', [trace], layout) - - // make time-series plot - let tdata = [] - let layout2 = { - title: 'Time series', - yaxis: { - title: 'App. res. [Ohm.m]' - }, - xaxis: { - title: 'Sampling time' - } - } - Plotly.newPlot('ts', tdata, layout2) - - // add trace to time-series 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-series plot - function removeTracesBtnFunc() { - squads = [] - tdata = [] - Plotly.newPlot('ts', tdata, layout2) - } - let removeTracesBtn = document.getElementById('removeTracesBtn') - removeTracesBtn.addEventListener('click', removeTracesBtnFunc) - - // getData - function getData() { - sendCommand(JSON.stringify({ - 'command': 'getData', - 'surveyNames': Object.keys(data).slice(0, -1) - // 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 assignment (magic! :o) - ...data, - ...ddic['data'] // value from second dic are preferred - } - - // dropdown with number of surveys and +++ - let surveyNames = Object.keys(data).sort() - - // callback function to draw the plot - function surveySelectFunc(el) { - let surveyName = el['target'].value - let df = data[surveyName] - if (df != undefined) { - let a = df['a'] - let b = df['b'] - let m = df['m'] - let n = df['n'] - // let's assume electrodes are 1 m distance - // compute pseudo-depth (assume no topo) - let xpos = [] - let ypos = [] - for (let i = 0; i < a.length; i++) { - let ab = (a[i] + b[i])/2 - let mn = (m[i] + n[i])/2 - let dist = Math.abs(ab - mn) - xpos.push(Math.min(ab, mn) + dist/2) - ypos.push(Math.sqrt(2)/2*dist) - } - // update the trace and redraw the figure - trace['x'] = xpos - trace['y'] = ypos - trace['marker']['color'] = df['rho'] - Plotly.redraw('gd') - } - } - - let surveySelect = document.getElementById('surveySelect') - - // 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 programmatically changing the value does not trigger the event) - surveySelectFunc({'target': surveySelect}) - - // update list of quadrupoles if any - if (quads.length == 0) { - console.log('updating list of quadrupoles') - 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-series 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 dictionary - tdata[k]['x'] = x - tdata[k]['y'] = y - } - //console.log(tdata) - Plotly.redraw('ts') - } - }) - } - let getDataBtn = document.getElementById('getDataBtn') - getDataBtn.addEventListener('click', getData) - - // 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('{"command": "removeData"}',function(x) { - data = {} - output.innerHTML = 'Status: ' + x['status'] + ' (all data cleared)' - console.log('all data removed') - }) - } - let removeDataBtn = document.getElementById('removeDataBtn') - removeDataBtn.addEventListener('click', removeDataBtnFunc) - - // invert data - // function invertBtnFunc() { - // sendCommand('{"command": "invert"}', function(x) { - // console.log('inversion results', x) - // }) - // } - // let invertBtn = document.getElementById('invertBtn') - // invertBtn.addEventListener('click', invertBtnFunc) - - // download data - function downloadBtnFunc() { - sendCommand('{"command": "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> +<!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="setConfigBtn" type="button" class="btn btn-secondary" data-toggle="modal" data-target="#exampleModal">Settings</button> + <button id='startBtn' type="button" class="btn btn-primary">Start</button> + <button id='stopBtn' type="button" class="btn btn-warning">Stop</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="0"/> + <input id="cmax" type="number" value="150"/> + <button id="capplyBtn" type="button" class="btn btn-info">Apply</button> + <div id="gd"></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="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 configuration --> + <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 configuration</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> + </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 + + // useful functions + function sendCommand(query, callback=null) { + // dic in the form: {'command': 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) + } + + // start button + function startBtnFunc() { + sendCommand('{"command": "start"}', function(x) { + console.log(x['status']) + if (x['status'] == 'running') { + output.innerHTML = 'Status: measuring...' + } + }) + } + let startBtn = document.getElementById('startBtn') + startBtn.addEventListener('click', startBtnFunc) + + // stop button + function stopBtnFunc() { + sendCommand('{"command": "stop"}', function(x) { + output.innerHTML = 'Status: ' + x['status'] + clearInterval(interv) + getData() + }) + } + let stopBtn = document.getElementById('stopBtn') + stopBtn.addEventListener('click', stopBtnFunc) + + // 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 + } + console.log(formVals) + + // define callback to send settigs to Pi + function configCallback() { + sendCommand(JSON.stringify({ + 'command': 'setConfig', + 'config': formVals + }), function(x) { + console.log('setconfig:', 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) + configCallback() + }, false) + } else { + console.log('no sequence uploaded') + formVals['sequence'] = '' + configCallback() + } + + + } + let saveConfigBtn = document.getElementById('saveConfigBtn') + saveConfigBtn.addEventListener('click', saveConfigBtnFunc) + + // make pseudo plot + var trace = { + x: [], + y: [], + mode: 'markers', + marker: { + size: 40, + color: [], + colorbar: { + title: 'App. res. [Ohm.m]', + cmin: 0, + cmax: 100, + } + } + } + let layout = { + title: 'Pseudo-section', + yaxis: { + title: 'Pseudo-depth', + autorange: 'reversed' + }, + xaxis: { + title: 'X' + } + + } + Plotly.newPlot('gd', [trace], layout) + + // make time-serie plot + let tdata = [] + let layout2 = { + title: 'Time-serie', + yaxis: { + title: 'App. res. [Ohm.m]' + }, + xaxis: { + title: 'Sampling time' + } + } + Plotly.newPlot('ts', tdata, layout2) + + // 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 a = df['a'] + let b = df['b'] + let m = df['m'] + let n = df['n'] + // 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 < a.length; i++) { + let ab = (a[i] + b[i])/2 + let mn = (m[i] + n[i])/2 + let dist = Math.abs(ab - mn) + 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) + } + 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') + + // bar chart for contact resistance + let rsdata = [] + let rslayout = { + title: 'Contact resistances', + yaxis: { + title: 'Resistance [kOhm]' + }, + xaxis: { + title: 'Consecutive electrodes' + } + } + Plotly.newPlot('rs', rsdata, rslayout) + + // run RS check + function rsBtnFunc() { + sendCommand('{"command": "rsCheck"}', function (res) { + // update the bar plot + rsdata.push({ + x: res['data']['AB'], + y: res['data']['res'], + name: 'RS', + type: 'bar' + }) + Plotly.redraw('rs') + }) + } + let rsBtn = document.getElementById('rsBtn') + rsBtn.addEventListener('click', rsBtnFunc) + + // clear RS graph + function rsClearBtnFunc() { + rsdata = [] + Plotly.newPlot('rs', rsdata, rslayout) + } + let rsClearBtn = document.getElementById('rsClearBtn') + rsClearBtn.addEventListener('click', rsClearBtnFunc) + + // getData + function getData() { + sendCommand(JSON.stringify({ + 'command': 'getData', + 'surveyNames': Object.keys(data).slice(0, -1) + // 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 and +++ + let 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 + if (quads.length == 0) { + console.log('updating list of quadrupoles') + 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('{"command": "removeData"}',function(x) { + data = {} + output.innerHTML = 'Status: ' + x['status'] + ' (all data cleared)' + console.log('all data removed') + }) + } + let removeDataBtn = document.getElementById('removeDataBtn') + removeDataBtn.addEventListener('click', removeDataBtnFunc) + + // shutdown Pi + function shutdownBtnFunc() { + sendCommand('{"command": "shutdown"}', function(x) { + console.log('shuting down...') + }) + } + let shutdownBtn = document.getElementById('shutdownBtn') + shutdownBtn.addEventListener('click', shutdownBtnFunc) + + // restart Pi + function restartBtnFunc() { + sendCommand('{"command": "restart"}', function(x) { + console.log('rebooting...') + }) + } + let restartBtn = document.getElementById('restartBtn') + restartBtn.addEventListener('click', restartBtnFunc) + + // invert data + // function invertBtnFunc() { + // sendCommand('{"command": "invert"}', function(x) { + // console.log('inversion results', x) + // }) + // } + // let invertBtn = document.getElementById('invertBtn') + // invertBtn.addEventListener('click', invertBtnFunc) + + // download data + function downloadBtnFunc() { + sendCommand('{"command": "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/install_local_mqtt_broker.sh b/install_local_mqtt_broker.sh old mode 100644 new mode 100755 diff --git a/ohmpi.py b/ohmpi.py index 44450d3409f0db34c2aa5581d39a483bae293830..6dfe8329471d8f4ae3f4e359d6415cc62f321f37 100644 --- a/ohmpi.py +++ b/ohmpi.py @@ -120,7 +120,7 @@ class OhmPi(object): - injection_duration (in seconds) - nbr_meas (total number of times the sequence will be 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) Parameters @@ -375,7 +375,7 @@ class OhmPi(object): # TODO here we can add the current_injected or voltage_injected in mA or mV # check arguments if nb_stack is None: - nb_stack = self.pardict['stack'] + nb_stack = self.pardict['nb_stack'] if injection_duration is None: injection_duration = self.pardict['injection_duration'] @@ -410,8 +410,7 @@ class OhmPi(object): gain_voltage = self.gain_auto(AnalogIn(self.ads_voltage, ads.P0, ads.P1)) pin0.value = False pin1.value = False - print(gain_current) - print(gain_voltage) + print('gain current: {:.3f}, gain voltage: {:.3f}'.format(gain_current, gain_voltage)) 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) @@ -465,20 +464,20 @@ class OhmPi(object): # create a dictionary and compute averaged values from all stacks d = { - "time": [datetime.now().isoformat()], + "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] + "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 } # round number to two decimal for nicer string output @@ -500,18 +499,45 @@ class OhmPi(object): """ Check contact resistance. """ # 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([ - np.arange(nelec - 1) + 1, - np.arange(nelec - 1) + 2, - np.arange(nelec - 1) + 1, - np.arange(nelec - 1) + 2 + elec[:-1], + elec[1:], + elec[:-1], + elec[1:], ]).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]): 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.direction = Direction.OUTPUT pin1 = self.mcp.get_pin(1) @@ -519,40 +545,51 @@ class OhmPi(object): pin0.value = False pin1.value = False - print(quad) # 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) # ADS1115 for voltage measurement (MN) 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 time.sleep(0.2) + + # measure current and voltage current = AnalogIn(self.ads_current, ads.P0).voltage / (50 * self.r_shunt) voltage = -AnalogIn(self.ads_voltage, ads.P0, ads.P1).voltage * 2.5 resistance = voltage / current - # print(B) - # print(A) - print(abs(round(resistance / 1000, 1)), "kOhm") + + # compute resistance measured (= contact resistance) + 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) pin0.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 or we offer the possiblity in 'run_measurement' to have rs_check each time? @@ -616,7 +653,7 @@ class OhmPi(object): # run a measurement 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"]) else: # for testing, generate random data current_measurement = { @@ -680,7 +717,7 @@ if on_pi: # and emit a warning otherwise if not arm64_imports: 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: print(colored(f'Not running on the Raspberry Pi platform.\nFor simulation purposes only...', 'yellow')) diff --git a/ohmpi_param.json b/ohmpi_param.json index 7071487a858d80f5fa2bec82abde9eec12c3a328..c2c68558a80f39841ea5d17858a2900ca4f9d454 100644 --- a/ohmpi_param.json +++ b/ohmpi_param.json @@ -3,6 +3,6 @@ "injection_duration":0.2, "nbr_meas": 100, "sequence_delay": 1, - "stack": 1, + "nb_stack": 1, "export_path": "data/measurement.csv" } diff --git a/run.sh b/run.sh old mode 100644 new mode 100755 index 61eb5c72bd2127c03a2529bde5a92e34b49bdc98..76b58dca433be66edff7bd1c39cb4323939ede9a --- a/run.sh +++ b/run.sh @@ -1,2 +1,2 @@ source ./ohmpy/bin/activate -python3 webserver.py +python webserver.py diff --git a/runOnStart.sh b/runOnStart.sh new file mode 100755 index 0000000000000000000000000000000000000000..801ecbaa9986b26597ce0e4facb98326cf3e5225 --- /dev/null +++ b/runOnStart.sh @@ -0,0 +1,2 @@ +echo "# start OhmPi web interface" >> $HOME/.bashrc +echo "(cd $HOME/OhmPi; ./run.sh)" >> $HOME/.bashrc diff --git a/webserver.py b/webserver.py index f2f85f596ab86e90f2676ec87167a7a4eaedef02..8171a5a93a08b45a3b265488e4c334778d0cc98b 100644 --- a/webserver.py +++ b/webserver.py @@ -1,99 +1,123 @@ -from http.server import SimpleHTTPRequestHandler, HTTPServer -# import time -import os -import json -from ohmpi import OhmPi -# import threading -import pandas as pd -import shutil - -hostName = 'localhost' -serverPort = 8080 - -# https://gist.github.com/MichaelCurrie/19394abc19abd0de4473b595c0e37a3a - -with open('ohmpi_param.json') as json_file: - pardict = json.load(json_file) - -ohmpi = OhmPi(pardict) - - -class MyServer(SimpleHTTPRequestHandler): - # because we use SimpleHTTPRequestHandler, we do not need to implement - # the do_GET() method (if we use the BaseHTTPRequestHandler, we would need to) - - # def do_GET(self): - # # normal get for wepages (not so secure!) - # print(self.command) - # print(self.headers) - # print(self.request) - # self.send_response(200) - # self.send_header("Content-type", "text/html") - # self.end_headers() - # with open(os.path.join('.', self.path[1:]), 'r') as f: - # self.wfile.write(bytes(f.read(), "utf-8")) - - def do_POST(self): - # global ohmpiThread, status, run - - dic = json.loads(self.rfile.read(int(self.headers['Content-Length']))) - rdic = {} - if dic['command'] == 'start': - ohmpi.measure() - elif dic['command'] == 'stop': - ohmpi.stop() - elif dic['command'] == 'getData': - # get all .csv file in data folder - fnames = os.listdir('data/') - ddic = {} - for fname in fnames: - if fname.replace('.csv', '') not in dic['surveyNames'] and fname != 'readme.txt': - 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['command'] == 'removeData': - shutil.rmtree('data') - os.mkdir('data') - elif dic['command'] == 'setConfig': - ohmpi.stop() - cdic = dic['config'] - ohmpi.pardict['nb_electrodes'] = int(cdic['nbElectrodes']) - ohmpi.pardict['injection_duration'] = float(cdic['injectionDuration']) - ohmpi.pardict['nbr_meas'] = int(cdic['nbMeasurements']) - ohmpi.pardict['stack'] = int(cdic['nbStack']) - ohmpi.pardict['sequence_delay'] = int(cdic['sequenceDelay']) - print('setConfig', ohmpi.pardict) - elif dic['command'] == 'invert': - pass - elif dic['command'] == 'getResults': - pass - elif dic['command'] == 'download': - shutil.make_archive('data', 'zip', 'data') - 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.") +from http.server import SimpleHTTPRequestHandler, HTTPServer +import time +import os +import json +from ohmpi import OhmPi +import threading +import pandas as pd +import shutil + +hostName = "raspberrypi.local" # works for AP-STA +#hostName = "192.168.50.1" # fixed IP in AP-STA mode +#hostName = "0.0.0.0" # for AP mode (not AP-STA) +serverPort = 8080 + +# https://gist.github.com/MichaelCurrie/19394abc19abd0de4473b595c0e37a3a + +with open('ohmpi_param.json') as json_file: + pardict = json.load(json_file) + +ohmpi = OhmPi(pardict, sequence='breadboard.txt') +#ohmpi = OhmPi(pardict, sequence='dd16s0no8.txt') + + +class MyServer(SimpleHTTPRequestHandler): + # because we use SimpleHTTPRequestHandler, we do not need to implement + # the do_GET() method (if we use the BaseHTTPRequestHandler, we would need to) + + # def do_GET(self): + # # normal get for wepages (not so secure!) + # print(self.command) + # print(self.headers) + # print(self.request) + # self.send_response(200) + # self.send_header("Content-type", "text/html") + # self.end_headers() + # with open(os.path.join('.', self.path[1:]), 'r') as f: + # self.wfile.write(bytes(f.read(), "utf-8")) + + def do_POST(self): + global ohmpiThread, status, run + dic = json.loads(self.rfile.read(int(self.headers['Content-Length']))) + rdic = {} + if dic['command'] == 'start': + ohmpi.measure() + elif dic['command'] == 'stop': + ohmpi.stop() + elif dic['command'] == '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.replace('.csv', '') not in dic['surveyNames'] + and fname != 'readme.txt' + and '_rs' not in fname): + 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['command'] == 'removeData': + shutil.rmtree('data') + os.mkdir('data') + elif dic['command'] == 'setConfig': + ohmpi.stop() + cdic = dic['config'] + ohmpi.pardict['nb_electrodes'] = int(cdic['nbElectrodes']) + ohmpi.pardict['injection_duration'] = float(cdic['injectionDuration']) + ohmpi.pardict['nbr_meas'] = int(cdic['nbMeasurements']) + ohmpi.pardict['nb_stack'] = int(cdic['nbStack']) + ohmpi.pardict['sequence_delay'] = int(cdic['sequenceDelay']) + if cdic['sequence'] != '': + with open('sequence.txt', 'w') as f: + f.write(cdic['sequence']) + ohmpi.read_quad('sequence.txt') + print('new sequence set.') + print('setConfig', ohmpi.pardict) + elif dic['command'] == 'invert': + pass + elif dic['command'] == 'getResults': + pass + elif dic['command'] == 'rsCheck': + ohmpi.rs_check() + fnames = sorted([fname for fname in os.listdir('data/') if fname[-7:] == '_rs.csv']) + df = pd.read_csv('data/' + fnames[-1]) + ddic = { + 'AB': (df['A'].astype('str') + '-' + df['B'].astype(str)).tolist(), + 'res': df['RS [kOhm]'].tolist() + } + rdic['data'] = ddic + elif dic['command'] == 'download': + shutil.make_archive('data', 'zip', 'data') + elif dic['command'] == 'shutdown': + print('shutting down...') + os.system('shutdown now -h') + 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.")