Commit ade26da0 authored by Arnaud WATLET's avatar Arnaud WATLET
Browse files

Merge branch 'code_refactor' of https://gitlab.irstea.fr/reversaal/OhmPi into code_refactor

Showing with 576 additions and 731 deletions
+576 -731
import logging
from ohmpi.utils import get_platform
from paho.mqtt.client import MQTTv31 # noqa
_, on_pi = get_platform()
# DEFINE THE ID OF YOUR OhmPi
ohmpi_id = '0001' if on_pi else 'XXXX'
# DEFINE YOUR MQTT BROKER (DEFAULT: 'localhost')
mqtt_broker = 'localhost' if on_pi else 'NAME_YOUR_BROKER_WHEN_IN_SIMULATION_MODE_HERE'
# DEFINE THE SUFFIX TO ADD TO YOUR LOGS FILES
logging_suffix = ''
# OhmPi configuration
OHMPI_CONFIG = {
'id': ohmpi_id, # Unique identifier of the OhmPi board (string)
'settings': 'ohmpi_settings.json', # INSERT YOUR FAVORITE SETTINGS FILE HERE
}
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',
},
'rx': {'model': 'mb_2023_0_X',
'coef_p2': 2.50, # slope for conversion for ADS, measurement in V/V
'sampling_rate': 50., # number of samples per second
'interface_name': 'i2c',
},
'mux': # default properties given in config are system properties that will be
# overwritten by properties defined in each the board dict below.
# if defined in board specs, values out of specs will be bounded to remain in specs
# omitted properties in config will be set to board specs default values if they exist
{'boards':
{'mux_A':
{'model': 'mux_2023_0_X',
'mux_tca_address': 0x70,
'roles': {'A': 'X'},
'cabling': {(i, j): ('mux_A', i) for j in ['A'] for i in range(1, 65)},
'voltage_max': 12.},
'mux_B':
{'model': 'mux_2023_0_X',
'mux_tca_address': 0x71,
'roles': {'B': 'X'},
'cabling': {(i, j): ('mux_B', i) for j in ['B'] for i in range(1, 65)},
'voltage_max': 12.},
'mux_M':
{'model': 'mux_2023_0_X',
'mux_tca_address': 0x72,
'roles': {'M': 'X'},
'cabling': {(i, j): ('mux_M', i) for j in ['M'] for i in range(1, 65)},
'voltage_max': 12.},
'mux_N':
{'model': 'mux_2023_0_X',
'mux_tca_address': 0x73,
'roles': {'N': 'X'},
'cabling': {(i, j): ('mux_N', i) for j in ['N'] for i in range(1, 65)},
'voltage_max': 12.},
},
'default': {'interface_name': 'i2c',
'voltage_max': 12.,
'current_max': 3.}
}
}
# SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS
# Execution logging configuration
EXEC_LOGGING_CONFIG = {
'logging_level': logging.DEBUG, # TODO: set logging level back to INFO
'log_file_logging_level': logging.DEBUG,
'logging_to_console': True,
'file_name': f'exec{logging_suffix}.log',
'max_bytes': 262144,
'backup_count': 30,
'when': 'd',
'interval': 1
}
# Data logging configuration
DATA_LOGGING_CONFIG = {
'logging_level': logging.INFO,
'logging_to_console': True,
'file_name': f'data{logging_suffix}.log',
'max_bytes': 16777216,
'backup_count': 1024,
'when': 'd',
'interval': 1
}
# State of Health logging configuration (For a future release)
SOH_LOGGING_CONFIG = {
'logging_level': logging.INFO,
'logging_to_console': True,
'log_file_logging_level': logging.DEBUG,
'file_name': f'soh{logging_suffix}.log',
'max_bytes': 16777216,
'backup_count': 1024,
'when': 'd',
'interval': 1
}
# MQTT logging configuration parameters
MQTT_LOGGING_CONFIG = {
'hostname': mqtt_broker,
'port': 1883,
'qos': 2,
'retain': False,
'keepalive': 60,
'will': None,
'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
'tls': None,
'protocol': MQTTv31,
'transport': 'tcp',
'client_id': f'{OHMPI_CONFIG["id"]}',
'exec_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/exec',
'exec_logging_level': logging.DEBUG,
'data_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/data',
'data_logging_level': DATA_LOGGING_CONFIG['logging_level'],
'soh_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/soh',
'soh_logging_level': SOH_LOGGING_CONFIG['logging_level']
}
# MQTT control configuration parameters
MQTT_CONTROL_CONFIG = {
'hostname': mqtt_broker,
'port': 1883,
'qos': 2,
'retain': False,
'keepalive': 60,
'will': None,
'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
'tls': None,
'protocol': MQTTv31,
'transport': 'tcp',
'client_id': f'{OHMPI_CONFIG["id"]}',
'ctrl_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/ctrl'
}
import logging
from ohmpi.utils import get_platform
from paho.mqtt.client import MQTTv31 # noqa
_, on_pi = get_platform()
# DEFINE THE ID OF YOUR OhmPi
ohmpi_id = '0001' if on_pi else 'XXXX'
# DEFINE YOUR MQTT BROKER (DEFAULT: 'localhost')
mqtt_broker = 'localhost' if on_pi else 'NAME_YOUR_BROKER_WHEN_IN_SIMULATION_MODE_HERE'
# DEFINE THE SUFFIX TO ADD TO YOUR LOGS FILES
logging_suffix = ''
# OhmPi configuration
OHMPI_CONFIG = {
'id': ohmpi_id, # Unique identifier of the OhmPi board (string)
'settings': 'ohmpi_settings.json', # INSERT YOUR FAVORITE SETTINGS FILE HERE
}
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',
},
'rx': {'model': 'mb_2023_0_X',
'coef_p2': 2.50, # slope for conversion for ADS, measurement in V/V
'sampling_rate': 50., # number of samples per second
'interface_name': 'i2c',
},
'mux': {'boards':
{'mux_02':
{'model': 'mux_2024_0_X',
'tca_address': None,
'tca_channel': 0,
'addr2': 'up',
'addr1': 'up',
# 'mcp_0': '0x26',
# 'mcp_1': '0x27',
'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
'cabling': {(i+8, j): ('mux_02', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
'voltage_max': 12.},
'mux_03':
{'model': 'mux_2024_0_X',
'tca_address': None,
'tca_channel': 0,
'addr2': 'down',
'addr1': 'up',
# 'mcp_0': '0x26',
# 'mcp_1': '0x27',
'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
'cabling': {(i+24, j): ('mux_03', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
'voltage_max': 12.},
'mux_05':
{'model': 'mux_2024_0_X',
'tca_address': None,
'tca_channel': 0,
'addr2': 'up',
'addr1': 'down',
'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
'cabling': {(i+0, j): ('mux_05', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
'voltage_max': 12.},
'mux_06':
{'model': 'mux_2024_0_X',
'tca_address': None,
'tca_channel': 0,
'addr2': 'down',
'addr1': 'down',
'roles': {'A': 'X', 'B': 'Y', 'M': 'XX', 'N': 'YY'},
'cabling': {(i+16, j): ('mux_06', i) for j in ['A', 'B', 'M', 'N'] for i in range(1, 9)},
'voltage_max': 12.},
},
'default': {'interface_name': 'i2c_ext',
'voltage_max': 100.,
'current_max': 3.}
}
}
# SET THE LOGGING LEVELS, MQTT BROKERS AND MQTT OPTIONS ACCORDING TO YOUR NEEDS
# Execution logging configuration
EXEC_LOGGING_CONFIG = {
'logging_level': logging.INFO,
'log_file_logging_level': logging.DEBUG,
'logging_to_console': True,
'file_name': f'exec{logging_suffix}.log',
'max_bytes': 262144,
'backup_count': 30,
'when': 'd',
'interval': 1
}
# Data logging configuration
DATA_LOGGING_CONFIG = {
'logging_level': logging.INFO,
'logging_to_console': True,
'file_name': f'data{logging_suffix}.log',
'max_bytes': 16777216,
'backup_count': 1024,
'when': 'd',
'interval': 1
}
# State of Health logging configuration (For a future release)
SOH_LOGGING_CONFIG = {
'logging_level': logging.INFO,
'logging_to_console': True,
'log_file_logging_level': logging.DEBUG,
'file_name': f'soh{logging_suffix}.log',
'max_bytes': 16777216,
'backup_count': 1024,
'when': 'd',
'interval': 1
}
# MQTT logging configuration parameters
MQTT_LOGGING_CONFIG = {
'hostname': mqtt_broker,
'port': 1883,
'qos': 2,
'retain': False,
'keepalive': 60,
'will': None,
'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
'tls': None,
'protocol': MQTTv31,
'transport': 'tcp',
'client_id': f'{OHMPI_CONFIG["id"]}',
'exec_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/exec',
'exec_logging_level': logging.DEBUG,
'data_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/data',
'data_logging_level': DATA_LOGGING_CONFIG['logging_level'],
'soh_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/soh',
'soh_logging_level': SOH_LOGGING_CONFIG['logging_level']
}
# MQTT control configuration parameters
MQTT_CONTROL_CONFIG = {
'hostname': mqtt_broker,
'port': 1883,
'qos': 2,
'retain': False,
'keepalive': 60,
'will': None,
'auth': {'username': 'mqtt_user', 'password': 'mqtt_password'},
'tls': None,
'protocol': MQTTv31,
'transport': 'tcp',
'client_id': f'{OHMPI_CONFIG["id"]}',
'ctrl_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/ctrl'
}
# we need to start the OhmPi instance so that it listens
# to message from the MQTT broker
import subprocess
# launch webserver
from ohmpi.utils import change_config
change_config('../configs/config_mb_2023.py', verbose=False)
from ohmpi.ohmpi import OhmPi
from ohmpi.config import OHMPI_CONFIG
ohmpi = OhmPi(settings=OHMPI_CONFIG['settings'])
if ohmpi.controller is not None:
ohmpi.controller.loop_forever()
# restore default config
change_config('../configs/config_default.py', verbose=False)
#
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('TkAgg')
import numpy as np
from ohmpi.utils import change_config
change_config('../configs/config_mb_2023.py', verbose=False)
from ohmpi.ohmpi import OhmPi
k = OhmPi()
k.run_inversion(['measurement_20220206T194552.csv'])
# single inversion
#k.run_inversion(['measurement_20220206T194552.csv'])
# batch inversion
xzv = k.run_inversion([
'measurement_20220206T194752.csv',
'measurement_20220206T194852.csv',
], reg_mode=0)
# make a contour figure with the output
fig, axs = plt.subplots(len(xzv), 1, sharex=True, sharey=True, figsize=(10, 6))
axs = [axs] if len(xzv) == 1 else axs
for i, dic in enumerate(xzv):
ax = axs[i]
x, z = np.meshgrid(dic['x'], dic['z'])
cax = ax.contourf(x, z, dic['rho'])
fig.colorbar(cax, ax=ax, label=r'$\rho$ [$\Omega$.m]')
plt.show(block=True)
# restore default config
change_config('../configs/config_default.py', verbose=False)
import matplotlib
matplotlib.use('TkAgg')
from ohmpi.utils import change_config
change_config('../configs/config_mb_2023_4_mux_2023.py', verbose=False)
import importlib
import os
import time
import logging
from ohmpi.config import HARDWARE_CONFIG
stand_alone = False
part_of_hardware_system = False
within_ohmpi = True
# Stand alone
if stand_alone:
ctl_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["ctl"].pop("model")}')
pwr_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["pwr"].pop("model")}')
tx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["tx"].pop("model")}')
rx_module = importlib.import_module(f'ohmpi.hardware_components.{HARDWARE_CONFIG["rx"].pop("model")}')
ctl = ctl_module.Ctl()
HARDWARE_CONFIG['tx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
HARDWARE_CONFIG['rx'].update({'ctl': ctl, 'exec_logger': ctl.exec_logger, 'soh_logger': ctl.soh_logger})
HARDWARE_CONFIG['tx'].update({'connection': HARDWARE_CONFIG['tx'].pop('connection',
ctl.interfaces[
HARDWARE_CONFIG['tx'].pop(
'interface_name', 'i2c')])})
HARDWARE_CONFIG['rx'].update({'connection': HARDWARE_CONFIG['rx'].pop('connection',
ctl.interfaces[
HARDWARE_CONFIG['rx'].pop(
'interface_name', 'i2c')])})
HARDWARE_CONFIG['pwr'].update({'connection': HARDWARE_CONFIG['pwr'].pop('connection',
ctl.interfaces[
HARDWARE_CONFIG['pwr'].pop(
'interface_name', None)])})
rx = rx_module.Rx(**HARDWARE_CONFIG['rx'])
tx = tx_module.Tx(**HARDWARE_CONFIG['tx'])
pwr = pwr_module.Pwr(**HARDWARE_CONFIG['pwr'])
role = 'A'
mux_id = f'mux_{role}'
mux_boards = []
mux_module = importlib.import_module(
f'ohmpi.hardware_components.{HARDWARE_CONFIG["mux"]["boards"][mux_id].pop("model")}')
MUX_CONFIG = HARDWARE_CONFIG['mux']['boards'][mux_id]
MUX_CONFIG.update({'ctl': ctl, 'connection': MUX_CONFIG.pop('connection', ctl.interfaces[
MUX_CONFIG.pop('interface_name', 'i2c_ext')]), 'exec_logger': ctl.exec_logger,
'soh_logger': ctl.soh_logger})
MUX_CONFIG.update({'id': mux_id})
mux = mux_module.Mux(**MUX_CONFIG)
mux.reset()
mux.test({role: [i for i in range(1, 65)]}, activation_time=.5)
mux.reset()
# mux as part of a OhmPiHardware system
if part_of_hardware_system:
from ohmpi.hardware_system import OhmPiHardware
print('Starting test of as part of an OhmPiHardware system.')
# mux_id = 'mux_03'
k = OhmPiHardware()
k.exec_logger.setLevel(logging.DEBUG)
# Test mux switching
k.reset_mux()
# k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='on')
# time.sleep(1.)
# k.switch_mux(electrodes=[1, 4, 2, 3], roles=['A', 'B', 'M', 'N'], state='off')
# k.mux_boards[mux_id].test(activation_time=.4)
k.test_mux()
k.reset_mux()
if within_ohmpi:
from ohmpi.ohmpi import OhmPi
# from ohmpi.plots import plot_exec_log
print('Starting test with OhmPi.')
k = OhmPi()
k.load_sequence(os.path.join(os.path.dirname(__file__), '../sequences/01_GRAD_8_s4_a4.txt'))
k.reset_mux()
# k.test_mux(mux_id=None, activation_time=0.2)
# k._hw.switch_mux([A, B, M, N], state='on')
# k._hw.vab_square_wave(12.,1., cycles=2)
# k._hw.switch_mux([A, B, M, N], state='off')
# k._hw.calibrate_rx_bias() # electrodes 1 4 2 3 should be connected to a reference circuit
# k._hw.rx._bias = -1.38
# print(f'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._hw.switch_mux([A, B, M, N], state='on')
# k._hw.vab_square_wave(12., cycle_duration=10., cycles=3)
# 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()
A, B, M, N = (1, 4, 2, 3)
d = k.run_measurement([A, B, M, N], injection_duration=2., nb_stack=2, duty_cycle=0.5)
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(save_fig=False)
# plot_exec_log('ohmpi/logs/exec.log')
change_config('../configs/config_default.py', verbose=False)
<!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">&#9654</button>
<button id='stopBtn' type="button" class="btn btn-warning">&#9724</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">&times;</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>
<!DOCTYPE html>
<!-- debugging instruction to see the messages passing on the RPi
mosquitto_sub -h raspberrypi.local -t ohmpi_0001/ctrl
-->
<html>
<head>
<meta charset="utf8"/>
......@@ -40,14 +43,14 @@
<input id="cmax" type="number" value="150"/>
<button id="capplyBtn" type="button" class="btn btn-info">Apply</button>
<div id="gd"></div>
<!-- trace figure -->
<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>
......@@ -57,9 +60,22 @@
<button id="rsClearBtn" type="button" class="btn btn-info">Clear plot</button>
<div id="rs"></div>
<!-- Inversion plot -->
<select id="surveySelectInv" class='custom-select'>
<option>measurement_20220206T194552.csv</option>
</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"/>
<button id="capplyBtnInv" type="button" class="btn btn-info">Apply</button>
<div id="inv"></div>
<!-- Additional buttons -->
<button id="downloadBtn" type="button" class="btn btn-primary">Download data</button>
<a id="download"></a>
<button id="restartBtn" type="button" class="btn btn-danger">Restart</button>
<button id="shutdownBtn" type="button" class="btn btn-danger">Shutdown</button>
<!-- Modal for configuration -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
......@@ -124,9 +140,7 @@
</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>
<footer>v0.3.0</footer>
</div>
<script type="text/javascript">
......@@ -142,6 +156,15 @@
let squads = [] // selected quadrupoles for time-serie figure
let commands = {} // store commands and their id
let callbacks = {} // store callback (might not be needed)
let invertData = [{
rho: [[10, 10.625, 12.5, 15.625, 20],
[5.625, 6.25, 8.125, 11.25, 15.625],
[2.5, 3.125, 5., 8.125, 12.5],
[0.625, 1.25, 3.125, 6.25, 10.625],
[0, 0.625, 2.5, 5.625, 10]],
x: [-9, -6, -5 , -3, -1],
y: [0, 1, 4, 5, 7],
}] // store inverted data
// function with MQTT
let topic = 'ohmpi_0001' // we could change this through a drop-down to connect to a different ohmpi
......@@ -153,12 +176,15 @@
let clientId = 'ohmpi_0001_html'
let message = null
let msg = ''
let userName = 'jkl' // can be ask to the user
let password = 'jkl'
// create client
client = new Paho.MQTT.Client(hostname, port, clientId);
client.onConnectionLost = onConnectionLost;
client.onMessageArrived = onMessageArrived;
client.connect({onSuccess:onConnect});
client = new Paho.MQTT.Client(hostname, port, clientId)
client.onConnectionLost = onConnectionLost
client.onMessageArrived = onMessageArrived
client.connect({onSuccess: onConnect})
// client.connect({userName: username, password: password, onSuccess:onConnect})
function onConnect() {
console.log("onConnect")
......@@ -187,7 +213,7 @@
let ddic = JSON.parse(payload.split('INFO:')[1])
// check cmd_id is any
processData(ddic)
processMessage(ddic)
// usually these don't have a cmd_id so we are not sure when
......@@ -476,6 +502,19 @@
)
}
// processMessage
function processMessage(ddic) {
if ('status' in ddic) {
// acquisition related
processData(ddic)
} else {
// inversion related
console.log('--------', ddic)
let invertData = ddic[0]
showInvFunc()
}
}
// processData
function processData(ddic) {
// update status
......@@ -582,6 +621,58 @@
surveySelectFunc({'target': surveySelect})
})
// plot inverted data
function showInvFunc() {
let cmin = document.getElementById('cminInv').value
let cmax = document.getElementById('cmaxInv').value
var data = [{
z: invertData[0]['rho'],
x: invertData[0]['x'],
y: invertData[0]['z'],
type: 'contour',
colorscale: 'Jet',
autocontour: true, // set to false if cmin is given
contours: {
start: cmin,
end: cmax,
size: 10
},
}]
var layout = {
title: 'Inverted section',
yaxis: {
title: 'Z [m]',
},
xaxis: {
title: 'X [m]'
}
}
Plotly.newPlot('inv', data, layout)
}
//invert data
function invertBtnFunc() {
let survey_name = document.getElementById('surveySelectInv').value
sendCommand(JSON.stringify({
'cmd': 'run_inversion',
'kwargs': {
'survey_names': [survey_name]
}
}), function(x) {
console.log('inversion results', x)
})
}
let invertBtn = document.getElementById('invertBtn')
invertBtn.addEventListener('click', invertBtnFunc)
// apply new colorscale for inversion
let capplyBtnInv = document.getElementById('capplyBtnInv')
capplyBtnInv.addEventListener('click', function() {
showInvFunc()
})
// checkbox interaction for data download
function dataRetrievalCheckFunc(x) {
if (x['target'].checked == true) {
......@@ -622,15 +713,6 @@
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) {
......
......@@ -940,7 +940,7 @@ class OhmPi(object):
"""
# check if we have any files to be inverted
if len(survey_names) == 0:
self.exec_logger('No file to invert')
self.exec_logger.error('No file to invert')
return []
# check if user didn't provide a single string instead of a list
......@@ -957,12 +957,13 @@ class OhmPi(object):
pdir = os.path.dirname(__file__)
# import resipy if available
try:
from scipy.interpolate import griddata
import pandas as pd
import sys
sys.path.append(os.path.join(pdir, '../../resipy/src/'))
from resipy import Project
except Exception as e:
self.exec_logger.error('Cannot import ResIPy or Pandas, error: ' + str(e))
self.exec_logger.error('Cannot import ResIPy, scipy or Pandas, error: ' + str(e))
return []
# get absolule filename
......@@ -1001,18 +1002,29 @@ class OhmPi(object):
self.exec_logger.info('ResIPy: invert survey')
k.invert(param=kwargs)
# read data
# read data and regrid on a regular grid for a plotly contour plot
self.exec_logger.info('Reading inverted surveys')
k.getResults()
xzv = []
for m in k.meshResults:
df = m.df
x = np.linspace(df['X'].min(), df['X'].max(), 20)
z = np.linspace(df['Z'].min(), df['Z'].max(), 20)
grid_x, grid_z = np.meshgrid(x, z)
grid_z = griddata(df[['X', 'Z']].values, df['Resistivity(ohm.m)'].values,
(grid_x, grid_z), method='linear')
# set nan to -1
inan = np.isnan(grid_z)
grid_z[inan] = -1
xzv.append({
'x': df['X'].values.tolist(),
'z': df['Z'].values.tolist(),
'rho': df['Resistivity(ohm.m)'].values.tolist(),
'x': x.tolist(),
'z': z.tolist(),
'rho': grid_z.tolist(),
})
self.data_logger.info(json.dumps(xzv)) # limited to one survey
return xzv
# Properties
......
RPi.GPIO
adafruit-blinka
adafruit-circuitpython-ads1x15
adafruit-circuitpython-tca9548a
adafruit-circuitpython-mcp230xx
adafruit_extended_bus
minimalmodbus
gpiozero
numpy
paho-mqtt
termcolor
pandas
matplotlib
scipy
requests
psutil
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment