diff --git a/PCB_file_measurement_card/measurement_board/kicad_v5.00/VMN/#auto_saved_files# b/PCB_file_measurement_card/measurement_board/kicad_v5.00/VMN/#auto_saved_files# index e4157d4aa08307d653dad6a35abc3bb36bb494e7..4e3cbf713672a3ea8841481da91a7f79411de2e3 100644 --- a/PCB_file_measurement_card/measurement_board/kicad_v5.00/VMN/#auto_saved_files# +++ b/PCB_file_measurement_card/measurement_board/kicad_v5.00/VMN/#auto_saved_files# @@ -1 +1 @@ -C:\Users\reclement\Documents\39_ohmpi\OhmPi\PCB_file_measurement_card\measurement_board\kicad_v5.00\VMN\_autosave-VMN.kicad_sch +/home/arnaud/codes/OhmPi/PCB_file_measurement_card/measurement_board/kicad_v5.00/VMN/_autosave-VMN.kicad_sch diff --git a/architecture.pdf b/architecture.pdf index a1025d0dccfa2174568c747a27517452214cd4bf..9c53a687468d97f5f0494d70b9e4fbf0cb3b9887 100644 Binary files a/architecture.pdf and b/architecture.pdf differ diff --git a/config.py b/config.py index 03ead8f48cfd6974218affa1e25fe1d0c0d895ca..8e206ce110071c5c746a4f832744e857f583184a 100644 --- a/config.py +++ b/config.py @@ -52,8 +52,9 @@ DATA_LOGGING_CONFIG = { # State of Health logging configuration SOH_LOGGING_CONFIG = { - 'file_name': 'soh.log', + 'logging_level' : logging.INFO, 'logging_to_console': True, + 'file_name': 'soh.log', 'max_bytes': 16777216, 'backup_count': 1024, 'when': 'd', @@ -74,8 +75,11 @@ MQTT_LOGGING_CONFIG = { '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', - 'soh_topic': f'ohmpi_{OHMPI_CONFIG["id"]}/soh' + '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 diff --git a/examples/basic_ohmpi_flows_node-red.json b/examples/basic_ohmpi_flows_node-red.json new file mode 100644 index 0000000000000000000000000000000000000000..e6184859f8a3875961dfb3ac1696d8cc9f6798ea --- /dev/null +++ b/examples/basic_ohmpi_flows_node-red.json @@ -0,0 +1,782 @@ +[ + { + "id": "b19c51e9d4d25a33", + "type": "tab", + "label": "Flow 1", + "disabled": false, + "info": "" + }, + { + "id": "0f23781293c4b819", + "type": "mqtt in", + "z": "b19c51e9d4d25a33", + "name": "", + "topic": "ohmpi_0001/exec", + "qos": "2", + "datatype": "auto-detect", + "broker": "6ae7e77e.04c64", + "nl": false, + "rap": false, + "inputs": 0, + "x": 390, + "y": 40, + "wires": [ + [ + "f4b6096e60252b62" + ] + ] + }, + { + "id": "36ba500fac1d0f38", + "type": "mqtt in", + "z": "b19c51e9d4d25a33", + "name": "", + "topic": "ohmpi_0001/data", + "qos": "2", + "datatype": "auto", + "broker": "6ae7e77e.04c64", + "nl": false, + "rap": false, + "inputs": 0, + "x": 380, + "y": 140, + "wires": [ + [ + "f6075b441607acc4", + "9465b94aca687c9f", + "f655ae35cc8d0036" + ] + ] + }, + { + "id": "16b2937ad25f25d2", + "type": "mqtt out", + "z": "b19c51e9d4d25a33", + "name": "MQTT ctrl", + "topic": "ohmpi_0001/ctrl", + "qos": "2", + "retain": "false", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "6ae7e77e.04c64", + "x": 820, + "y": 340, + "wires": [] + }, + { + "id": "c7c725c7f947f5a8", + "type": "mqtt in", + "z": "b19c51e9d4d25a33", + "name": "", + "topic": "ohmpi_0001/soh", + "qos": "2", + "datatype": "auto", + "broker": "6ae7e77e.04c64", + "nl": false, + "rap": false, + "inputs": 0, + "x": 380, + "y": 240, + "wires": [ + [ + "6d9397b8b510b4b8" + ] + ] + }, + { + "id": "06e401792488500e", + "type": "ui_button", + "z": "b19c51e9d4d25a33", + "name": "Run sequence", + "group": "142ad6ae.d55e29", + "order": 1, + "width": "1", + "height": "1", + "passthru": false, + "label": "⏺", + "tooltip": "run sequence", + "color": "red", + "bgcolor": "lightgrey", + "className": "", + "icon": "", + "payload": "{\"cmd_id\" :\"0\", \"cmd\":\"run_sequence_async\"}", + "payloadType": "str", + "topic": "topic", + "topicType": "msg", + "x": 380, + "y": 340, + "wires": [ + [ + "16b2937ad25f25d2" + ] + ] + }, + { + "id": "c427102c051828b6", + "type": "ui_button", + "z": "b19c51e9d4d25a33", + "name": "Interrupt", + "group": "142ad6ae.d55e29", + "order": 2, + "width": "1", + "height": "1", + "passthru": false, + "label": " ◾", + "tooltip": "interrupt sequence", + "color": "black", + "bgcolor": "lightgrey", + "className": "", + "icon": "", + "payload": "{\"cmd_id\" :\"0\", \"cmd\":\"interrupt\"}", + "payloadType": "str", + "topic": "topic", + "topicType": "msg", + "x": 360, + "y": 400, + "wires": [ + [ + "16b2937ad25f25d2" + ] + ] + }, + { + "id": "f4b6096e60252b62", + "type": "ui_text", + "z": "b19c51e9d4d25a33", + "group": "b0990b3c5ff3c09a", + "order": 2, + "width": "16", + "height": "3", + "name": "MQTT exec", + "label": "Execution", + "format": "{{msg.payload}}", + "layout": "row-spread", + "className": "", + "x": 830, + "y": 40, + "wires": [] + }, + { + "id": "f6075b441607acc4", + "type": "ui_text", + "z": "b19c51e9d4d25a33", + "group": "b0990b3c5ff3c09a", + "order": 3, + "width": "16", + "height": "3", + "name": "MQTT Data", + "label": "Data", + "format": "{{msg.payload}}", + "layout": "row-spread", + "className": "", + "x": 830, + "y": 140, + "wires": [] + }, + { + "id": "6d9397b8b510b4b8", + "type": "ui_text", + "z": "b19c51e9d4d25a33", + "group": "b0990b3c5ff3c09a", + "order": 4, + "width": 0, + "height": 0, + "name": "MQTT SOH", + "label": "SOH", + "format": "{{msg.payload}}", + "layout": "row-spread", + "className": "", + "x": 830, + "y": 240, + "wires": [] + }, + { + "id": "455f7fa404a19428", + "type": "ui_button", + "z": "b19c51e9d4d25a33", + "name": "", + "group": "b0990b3c5ff3c09a", + "order": 1, + "width": 0, + "height": 0, + "passthru": false, + "label": "clear messages", + "tooltip": "", + "color": "", + "bgcolor": "", + "className": "", + "icon": "", + "payload": " ", + "payloadType": "str", + "topic": "topic", + "topicType": "msg", + "x": 580, + "y": 160, + "wires": [ + [ + "6d9397b8b510b4b8", + "f6075b441607acc4", + "f4b6096e60252b62" + ] + ] + }, + { + "id": "de9e803de4d28986", + "type": "ui_dropdown", + "z": "b19c51e9d4d25a33", + "name": "", + "label": "command", + "tooltip": "", + "place": "Select option", + "group": "142ad6ae.d55e29", + "order": 5, + "width": 0, + "height": 0, + "passthru": true, + "multiple": false, + "options": [ + { + "label": "", + "value": "load_sequence", + "type": "str" + }, + { + "label": "", + "value": "reset_mux", + "type": "str" + }, + { + "label": "", + "value": "set_sequence", + "type": "str" + }, + { + "label": "", + "value": "update_settings", + "type": "str" + } + ], + "payload": "", + "topic": "command", + "topicType": "str", + "className": "", + "x": 120, + "y": 700, + "wires": [ + [ + "68222c7c6633d3fb" + ] + ] + }, + { + "id": "17f76c1517cb0285", + "type": "ui_text_input", + "z": "b19c51e9d4d25a33", + "name": "", + "label": "kwargs", + "tooltip": "", + "group": "142ad6ae.d55e29", + "order": 5, + "width": 0, + "height": 0, + "passthru": true, + "mode": "text", + "delay": "250", + "topic": "kwargs", + "sendOnBlur": false, + "className": "", + "topicType": "str", + "x": 119, + "y": 777, + "wires": [ + [ + "c25811ae6ee89f2d" + ] + ] + }, + { + "id": "c25811ae6ee89f2d", + "type": "json", + "z": "b19c51e9d4d25a33", + "name": "", + "property": "payload", + "action": "", + "pretty": false, + "x": 269, + "y": 777, + "wires": [ + [ + "071930f0cabf3d94" + ] + ] + }, + { + "id": "d84418dc2b7c5e26", + "type": "debug", + "z": "b19c51e9d4d25a33", + "name": "debug 5", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1140, + "y": 460, + "wires": [] + }, + { + "id": "91523713d9d4918e", + "type": "join", + "z": "b19c51e9d4d25a33", + "name": "", + "mode": "custom", + "build": "merged", + "property": "payload", + "propertyType": "msg", + "key": "topic", + "joiner": "\\n", + "joinerType": "str", + "accumulate": true, + "timeout": "", + "count": "3", + "reduceRight": false, + "reduceExp": "", + "reduceInit": "", + "reduceInitType": "", + "reduceFixup": "", + "x": 710, + "y": 480, + "wires": [ + [ + "0566358048ec3fdb", + "16b2937ad25f25d2", + "d84418dc2b7c5e26" + ] + ] + }, + { + "id": "0566358048ec3fdb", + "type": "ui_text", + "z": "b19c51e9d4d25a33", + "group": "142ad6ae.d55e29", + "order": 6, + "width": "2", + "height": "3", + "name": "", + "label": "Command to send", + "format": "{{msg.payload}}", + "layout": "row-spread", + "className": "", + "x": 850, + "y": 580, + "wires": [] + }, + { + "id": "68222c7c6633d3fb", + "type": "function", + "z": "b19c51e9d4d25a33", + "name": "set cmd", + "func": "var newMsg = { payload: {\"cmd\": msg.payload }};\nglobal.set(\"command_tmp\",newMsg)\nreturn global.get(\"command_tmp\");", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 280, + "y": 700, + "wires": [ + [] + ] + }, + { + "id": "2d858a81f7e7f2b3", + "type": "ui_button", + "z": "b19c51e9d4d25a33", + "name": "", + "group": "142ad6ae.d55e29", + "order": 7, + "width": 0, + "height": 0, + "passthru": false, + "label": "Send command", + "tooltip": "", + "color": "", + "bgcolor": "", + "className": "", + "icon": "", + "payload": "", + "payloadType": "str", + "topic": "topic", + "topicType": "msg", + "x": 140, + "y": 580, + "wires": [ + [ + "731085dbdb634028", + "d6ed89221d8bd997", + "0af4c77ea5f4fdf3", + "6e58cc6bf0e75a67" + ] + ] + }, + { + "id": "8571a140d714f9f3", + "type": "change", + "z": "b19c51e9d4d25a33", + "name": "", + "rules": [ + { + "t": "set", + "p": "complete", + "pt": "msg", + "to": "true", + "tot": "bool" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 570, + "y": 580, + "wires": [ + [ + "91523713d9d4918e" + ] + ] + }, + { + "id": "071930f0cabf3d94", + "type": "function", + "z": "b19c51e9d4d25a33", + "name": "set kwargs", + "func": "var newMsg = { payload: {\"kwargs\": msg.payload }};\nglobal.set(\"kwargs_tmp\", newMsg)\nreturn global.get(\"kwargs_tmp\");", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 429, + "y": 777, + "wires": [ + [] + ] + }, + { + "id": "d6ed89221d8bd997", + "type": "delay", + "z": "b19c51e9d4d25a33", + "name": "", + "pauseType": "delay", + "timeout": "250", + "timeoutUnits": "milliseconds", + "rate": "1", + "nbRateUnits": "1", + "rateUnits": "second", + "randomFirst": "1", + "randomLast": "5", + "randomUnits": "seconds", + "drop": false, + "allowrate": false, + "outputs": 1, + "x": 370, + "y": 580, + "wires": [ + [ + "8571a140d714f9f3" + ] + ] + }, + { + "id": "e25b5009b71765aa", + "type": "function", + "z": "b19c51e9d4d25a33", + "name": "set cmd_id", + "func": "var newMsg = { payload: {\"cmd_id\": msg.payload }};\nreturn newMsg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 470, + "y": 540, + "wires": [ + [ + "91523713d9d4918e" + ] + ] + }, + { + "id": "731085dbdb634028", + "type": "uuid", + "z": "b19c51e9d4d25a33", + "uuidVersion": "v1", + "namespaceType": "", + "namespace": "", + "namespaceCustom": "", + "name": "", + "field": "payload", + "fieldType": "msg", + "x": 290, + "y": 540, + "wires": [ + [ + "e25b5009b71765aa" + ] + ] + }, + { + "id": "63021563a27e162b", + "type": "ui_chart", + "z": "b19c51e9d4d25a33", + "name": "", + "group": "7792ecc419ecbb59", + "order": 3, + "width": "11", + "height": "7", + "label": "chart", + "chartType": "line", + "legend": "true", + "xformat": "HH:mm:ss", + "interpolate": "linear", + "nodata": "", + "dot": true, + "ymin": "", + "ymax": "", + "removeOlder": 1, + "removeOlderPoints": "", + "removeOlderUnit": "3600", + "cutout": 0, + "useOneColor": false, + "useUTC": false, + "colors": [ + "#1f77b4", + "#aec7e8", + "#ff7f0e", + "#2ca02c", + "#98df8a", + "#d62728", + "#ff9896", + "#9467bd", + "#c5b0d5" + ], + "outputs": 1, + "useDifferentColor": false, + "className": "", + "x": 1130, + "y": 180, + "wires": [ + [] + ] + }, + { + "id": "f655ae35cc8d0036", + "type": "function", + "z": "b19c51e9d4d25a33", + "name": "function 3", + "func": "var msg2 = { payload: JSON.parse(msg.payload.split(' | ')[2].slice(6,).split(`'`).join(`\"`))[\"R [ohm]\"] };\nreturn msg2;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 980, + "y": 180, + "wires": [ + [ + "d891753fb13281c2", + "63021563a27e162b" + ] + ] + }, + { + "id": "d891753fb13281c2", + "type": "debug", + "z": "b19c51e9d4d25a33", + "name": "debug 6", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1140, + "y": 240, + "wires": [] + }, + { + "id": "8b35e61a7f7a37f8", + "type": "ui_gauge", + "z": "b19c51e9d4d25a33", + "name": "", + "group": "7792ecc419ecbb59", + "order": 4, + "width": 0, + "height": 0, + "gtype": "gage", + "title": "Measured Resistances", + "label": "R [Ohm]", + "format": "{{value}}", + "min": 0, + "max": "50", + "colors": [ + "#00b500", + "#e6e600", + "#ca3838" + ], + "seg1": "", + "seg2": "", + "className": "", + "x": 1180, + "y": 100, + "wires": [] + }, + { + "id": "9465b94aca687c9f", + "type": "function", + "z": "b19c51e9d4d25a33", + "name": "function 4", + "func": "var msg2 = { payload: JSON.parse(msg.payload.split(' | ')[2].slice(6,).split(`'`).join(`\"`))[\"R [ohm]\"] };\nreturn msg2;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 980, + "y": 100, + "wires": [ + [ + "8b35e61a7f7a37f8", + "763ea2c282b8b81a" + ] + ] + }, + { + "id": "763ea2c282b8b81a", + "type": "debug", + "z": "b19c51e9d4d25a33", + "name": "debug 7", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 1160, + "y": 40, + "wires": [] + }, + { + "id": "0af4c77ea5f4fdf3", + "type": "function", + "z": "b19c51e9d4d25a33", + "name": "set cmd", + "func": "return global.get(\"command_tmp\");", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 460, + "y": 620, + "wires": [ + [ + "91523713d9d4918e" + ] + ] + }, + { + "id": "6e58cc6bf0e75a67", + "type": "function", + "z": "b19c51e9d4d25a33", + "name": "set kwargs", + "func": "return global.get(\"kwargs_tmp\");", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 470, + "y": 660, + "wires": [ + [ + "91523713d9d4918e" + ] + ] + }, + { + "id": "6ae7e77e.04c64", + "type": "mqtt-broker", + "name": "ohmpi_local_broker", + "broker": "127.0.0.1", + "port": "1883", + "clientid": "", + "autoConnect": true, + "usetls": false, + "compatmode": false, + "protocolVersion": "4", + "keepalive": "60", + "cleansession": true, + "birthTopic": "", + "birthQos": "0", + "birthPayload": "", + "birthMsg": {}, + "closeTopic": "", + "closeQos": "0", + "closePayload": "", + "closeMsg": {}, + "willTopic": "", + "willQos": "0", + "willPayload": "", + "willMsg": {}, + "userProps": "", + "sessionExpiry": "" + }, + { + "id": "142ad6ae.d55e29", + "type": "ui_group", + "name": "Buttons", + "tab": "5d888f29.07334", + "order": 3, + "disp": true, + "width": "6", + "collapse": false + }, + { + "id": "b0990b3c5ff3c09a", + "type": "ui_group", + "name": "Messages", + "tab": "5d888f29.07334", + "order": 2, + "disp": true, + "width": "16", + "collapse": true + }, + { + "id": "7792ecc419ecbb59", + "type": "ui_group", + "name": "Messages", + "tab": "5d888f29.07334", + "order": 2, + "disp": true, + "width": "16", + "collapse": true, + "className": "" + }, + { + "id": "5d888f29.07334", + "type": "ui_tab", + "name": "Home", + "icon": "dashboard", + "disabled": false, + "hidden": false + } +] diff --git a/examples/example_ohmpi_flows_node-red.json b/examples/example_ohmpi_flows_node-red.json deleted file mode 100644 index 3ee6c3d2dc504204697ba64857d465f67f6f044a..0000000000000000000000000000000000000000 --- a/examples/example_ohmpi_flows_node-red.json +++ /dev/null @@ -1,557 +0,0 @@ -[ - { - "id": "6fef03d5598b4c7a", - "type": "tab", - "label": "Flow 1", - "disabled": false, - "info": "" - }, - { - "id": "77db8af51a12093d", - "type": "mqtt in", - "z": "6fef03d5598b4c7a", - "name": "", - "topic": "ohmpi_0001/exec", - "qos": "2", - "datatype": "auto", - "broker": "5a09c1ee2a3419c3", - "nl": false, - "rap": false, - "inputs": 0, - "x": 410, - "y": 500, - "wires": [ - [ - "b30d9b90bda62986", - "3f46bb85053b613c" - ] - ] - }, - { - "id": "b30d9b90bda62986", - "type": "debug", - "z": "6fef03d5598b4c7a", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "payload", - "targetType": "msg", - "statusVal": "", - "statusType": "auto", - "x": 850, - "y": 500, - "wires": [] - }, - { - "id": "d9745ad9da06b862", - "type": "mqtt in", - "z": "6fef03d5598b4c7a", - "name": "", - "topic": "ohmpi_0001/data", - "qos": "2", - "datatype": "auto", - "broker": "5a09c1ee2a3419c3", - "nl": false, - "rap": false, - "inputs": 0, - "x": 400, - "y": 600, - "wires": [ - [ - "c5dc2f808ccac105", - "3ef697f4f3fdc90e", - "a67154e86b9510f8", - "1bc67267d61f8ac7" - ] - ] - }, - { - "id": "c5dc2f808ccac105", - "type": "debug", - "z": "6fef03d5598b4c7a", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "payload", - "targetType": "msg", - "statusVal": "", - "statusType": "auto", - "x": 850, - "y": 600, - "wires": [] - }, - { - "id": "bafb34347dc3236b", - "type": "mqtt out", - "z": "6fef03d5598b4c7a", - "name": "MQTT ctrl", - "topic": "ohmpi_0001/ctrl", - "qos": "2", - "retain": "false", - "respTopic": "", - "contentType": "", - "userProps": "", - "correl": "", - "expiry": "", - "broker": "5a09c1ee2a3419c3", - "x": 840, - "y": 180, - "wires": [] - }, - { - "id": "8dc72d9df180684c", - "type": "mqtt in", - "z": "6fef03d5598b4c7a", - "name": "", - "topic": "ohmpi_0001/soh", - "qos": "2", - "datatype": "auto", - "broker": "5a09c1ee2a3419c3", - "nl": false, - "rap": false, - "inputs": 0, - "x": 400, - "y": 700, - "wires": [ - [ - "9ecd9ccb499016d2" - ] - ] - }, - { - "id": "9ecd9ccb499016d2", - "type": "debug", - "z": "6fef03d5598b4c7a", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "payload", - "targetType": "msg", - "statusVal": "", - "statusType": "auto", - "x": 850, - "y": 700, - "wires": [] - }, - { - "id": "78c8945000eccf7d", - "type": "ui_button", - "z": "6fef03d5598b4c7a", - "name": "", - "group": "142ad6ae.d55e29", - "order": 0, - "width": "2", - "height": "1", - "passthru": false, - "label": "Run", - "tooltip": "", - "color": "", - "bgcolor": "green", - "className": "", - "icon": "", - "payload": "{\"cmd_id\" :\"0\", \"cmd\":\"run_sequence\"}", - "payloadType": "str", - "topic": "topic", - "topicType": "msg", - "x": 370, - "y": 80, - "wires": [ - [ - "bafb34347dc3236b" - ] - ] - }, - { - "id": "819bb1f07e51b8c6", - "type": "ui_button", - "z": "6fef03d5598b4c7a", - "name": "", - "group": "142ad6ae.d55e29", - "order": 0, - "width": "2", - "height": "1", - "passthru": false, - "label": "Interrupt", - "tooltip": "", - "color": "", - "bgcolor": "red", - "className": "", - "icon": "", - "payload": "{\"cmd_id\" :\"0\", \"cmd\":\"stop\"}", - "payloadType": "str", - "topic": "topic", - "topicType": "msg", - "x": 380, - "y": 140, - "wires": [ - [ - "bafb34347dc3236b" - ] - ] - }, - { - "id": "4ec24224cfd4e4e8", - "type": "ui_form", - "z": "6fef03d5598b4c7a", - "name": "Commands", - "label": "", - "group": "142ad6ae.d55e29", - "order": 4, - "width": 0, - "height": 0, - "options": [ - { - "label": "cmd_id", - "value": "cmd_id", - "type": "text", - "required": false, - "rows": null - }, - { - "label": "cmd", - "value": "cmd", - "type": "text", - "required": true, - "rows": null - }, - { - "label": "args", - "value": "args", - "type": "text", - "required": false, - "rows": null - } - ], - "formValue": { - "cmd_id": "", - "cmd": "", - "args": "" - }, - "payload": "", - "submit": "submit", - "cancel": "cancel", - "topic": "topic", - "topicType": "msg", - "splitLayout": "", - "className": "", - "x": 390, - "y": 340, - "wires": [ - [ - "547fb5791365f6f7", - "bafb34347dc3236b" - ] - ] - }, - { - "id": "3f46bb85053b613c", - "type": "ui_text", - "z": "6fef03d5598b4c7a", - "group": "7792ecc419ecbb59", - "order": 0, - "width": "16", - "height": "12", - "name": "MQTT exec", - "label": "Execution", - "format": "{{msg.payload}}", - "layout": "row-spread", - "className": "", - "x": 850, - "y": 540, - "wires": [] - }, - { - "id": "3ef697f4f3fdc90e", - "type": "ui_text", - "z": "6fef03d5598b4c7a", - "group": "7792ecc419ecbb59", - "order": 1, - "width": "16", - "height": "12", - "name": "MQTT Data", - "label": "Data", - "format": "{{msg.payload}}", - "layout": "row-spread", - "className": "", - "x": 850, - "y": 640, - "wires": [] - }, - { - "id": "547fb5791365f6f7", - "type": "debug", - "z": "6fef03d5598b4c7a", - "name": "debug 1", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 680, - "y": 360, - "wires": [] - }, - { - "id": "c7c6c06cfd2c4646", - "type": "ui_button", - "z": "6fef03d5598b4c7a", - "name": "", - "group": "142ad6ae.d55e29", - "order": 0, - "width": "2", - "height": "1", - "passthru": false, - "label": "Set sequence", - "tooltip": "", - "color": "", - "bgcolor": "green", - "className": "", - "icon": "", - "payload": "{\"cmd_id\":\"\",\"cmd\":\"set_sequence\",\"args\": \"5 6 7 8\\n10 11 12 13\"}", - "payloadType": "str", - "topic": "topic", - "topicType": "msg", - "x": 400, - "y": 200, - "wires": [ - [ - "bafb34347dc3236b" - ] - ] - }, - { - "id": "68a3f36a08839f62", - "type": "ui_button", - "z": "6fef03d5598b4c7a", - "name": "", - "group": "142ad6ae.d55e29", - "order": 0, - "width": "2", - "height": "1", - "passthru": false, - "label": "Update settings", - "tooltip": "", - "color": "", - "bgcolor": "green", - "className": "", - "icon": "", - "payload": "{\"cmd_id\":\"\",\"cmd\":\"update_settings\",\"args\":{\"sequence\":\"5 6 7 8\\n10 11 12 13\"}}", - "payloadType": "str", - "topic": "topic", - "topicType": "msg", - "x": 400, - "y": 260, - "wires": [ - [ - "bafb34347dc3236b" - ] - ] - }, - { - "id": "b7f0b15aea29d7d2", - "type": "ui_chart", - "z": "6fef03d5598b4c7a", - "name": "", - "group": "7792ecc419ecbb59", - "order": 3, - "width": "11", - "height": "7", - "label": "chart", - "chartType": "line", - "legend": "true", - "xformat": "HH:mm:ss", - "interpolate": "linear", - "nodata": "", - "dot": true, - "ymin": "", - "ymax": "", - "removeOlder": 1, - "removeOlderPoints": "", - "removeOlderUnit": "3600", - "cutout": 0, - "useOneColor": false, - "useUTC": false, - "colors": [ - "#1f77b4", - "#aec7e8", - "#ff7f0e", - "#2ca02c", - "#98df8a", - "#d62728", - "#ff9896", - "#9467bd", - "#c5b0d5" - ], - "outputs": 1, - "useDifferentColor": false, - "className": "", - "x": 1270, - "y": 640, - "wires": [ - [] - ] - }, - { - "id": "1bc67267d61f8ac7", - "type": "function", - "z": "6fef03d5598b4c7a", - "name": "function 1", - "func": "var msg2 = { payload: JSON.parse(msg.payload.split(' | ')[2].slice(6,).split(`'`).join(`\"`))[\"R [ohm]\"] };\nreturn msg2;", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 1120, - "y": 640, - "wires": [ - [ - "249d43079fd9a894", - "b7f0b15aea29d7d2" - ] - ] - }, - { - "id": "249d43079fd9a894", - "type": "debug", - "z": "6fef03d5598b4c7a", - "name": "debug 2", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 1280, - "y": 700, - "wires": [] - }, - { - "id": "24e6dbe73ec692f9", - "type": "ui_gauge", - "z": "6fef03d5598b4c7a", - "name": "", - "group": "7792ecc419ecbb59", - "order": 4, - "width": 0, - "height": 0, - "gtype": "gage", - "title": "Measured Resistances", - "label": "R [Ohm]", - "format": "{{value}}", - "min": 0, - "max": "50", - "colors": [ - "#00b500", - "#e6e600", - "#ca3838" - ], - "seg1": "", - "seg2": "", - "className": "", - "x": 1320, - "y": 560, - "wires": [] - }, - { - "id": "a67154e86b9510f8", - "type": "function", - "z": "6fef03d5598b4c7a", - "name": "function 2", - "func": "var msg2 = { payload: JSON.parse(msg.payload.split(' | ')[2].slice(6,).split(`'`).join(`\"`))[\"R [ohm]\"] };\nreturn msg2;", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 1120, - "y": 560, - "wires": [ - [ - "24e6dbe73ec692f9", - "df445e407bbcd544" - ] - ] - }, - { - "id": "df445e407bbcd544", - "type": "debug", - "z": "6fef03d5598b4c7a", - "name": "debug 3", - "active": false, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 1300, - "y": 500, - "wires": [] - }, - { - "id": "5a09c1ee2a3419c3", - "type": "mqtt-broker", - "name": "ohmpi_local", - "broker": "127.0.0.1", - "port": "1880", - "clientid": "", - "autoConnect": true, - "usetls": false, - "protocolVersion": "4", - "keepalive": "60", - "cleansession": true, - "birthTopic": "", - "birthQos": "0", - "birthPayload": "", - "birthMsg": {}, - "closeTopic": "", - "closeQos": "0", - "closePayload": "", - "closeMsg": {}, - "willTopic": "", - "willQos": "0", - "willPayload": "", - "willMsg": {}, - "userProps": "", - "sessionExpiry": "" - }, - { - "id": "142ad6ae.d55e29", - "type": "ui_group", - "name": "Buttons", - "tab": "5d888f29.07334", - "order": 3, - "disp": true, - "width": "6", - "collapse": false - }, - { - "id": "7792ecc419ecbb59", - "type": "ui_group", - "name": "Messages", - "tab": "5d888f29.07334", - "order": 2, - "disp": true, - "width": "16", - "collapse": true, - "className": "" - }, - { - "id": "5d888f29.07334", - "type": "ui_tab", - "name": "Home", - "icon": "dashboard", - "disabled": false, - "hidden": false - } -] \ No newline at end of file diff --git a/mqtt_controller.py b/examples/mqtt_controller.py similarity index 92% rename from mqtt_controller.py rename to examples/mqtt_controller.py index dd2e6a5c21636c82faa5294c961aa331d8137a65..a9d42e117e4ea2b1c877297bbc3086aeab882287 100644 --- a/mqtt_controller.py +++ b/examples/mqtt_controller.py @@ -22,13 +22,13 @@ settings = { } cmd_id = uuid.uuid4().hex -payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'update_settings', 'args': settings}) +payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'update_settings', 'kwargs': {'settings' : settings}}) print(f'Update settings setup message: {payload} to {publisher_config["topic"]} with config {publisher_config}') publish.single(payload=payload, **publisher_config) sequence = [[1, 2, 3, 4]] cmd_id = uuid.uuid4().hex -payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'set_sequence', 'args': sequence}) +payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'set_sequence', 'kwargs': {'sequence': sequence}}) print(f'Set sequence message: {payload} to {publisher_config["topic"]} with config {publisher_config}') publish.single(payload=payload, **publisher_config) cmd_id = uuid.uuid4().hex @@ -38,7 +38,7 @@ publish.single(payload=payload, **publisher_config) for i in range(4): cmd_id = uuid.uuid4().hex - payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'start'}) + payload = json.dumps({'cmd_id': cmd_id, 'cmd': 'run_sequence_async'}) print(f'Publishing message {i}: {payload} to {publisher_config["topic"]} with config {publisher_config}') publish.single(payload=payload, **publisher_config) time.sleep(1) diff --git a/logging_setup.py b/logging_setup.py index e0ced5e8f2d1ba14a9c6e89be15f8cd602187eb9..b80d01fcca272dc652418df4e520314e2769ee29 100644 --- a/logging_setup.py +++ b/logging_setup.py @@ -52,15 +52,18 @@ def setup_loggers(mqtt=True): if mqtt: mqtt_settings = MQTT_LOGGING_CONFIG.copy() - [mqtt_settings.pop(i) for i in ['client_id', 'exec_topic', 'data_topic', 'soh_topic']] + mqtt_exec_logging_level = mqtt_settings.pop('exec_logging_level', logging.DEBUG) + [mqtt_settings.pop(i) for i in ['client_id', 'exec_topic', 'data_topic', 'soh_topic', 'data_logging_level', + 'soh_logging_level']] mqtt_settings.update({'topic': MQTT_LOGGING_CONFIG['exec_topic']}) # TODO: handle the case of MQTT broker down or temporarily unavailable try: mqtt_exec_handler = MQTTHandler(**mqtt_settings) - mqtt_exec_handler.setLevel(EXEC_LOGGING_CONFIG['logging_level']) + mqtt_exec_handler.setLevel(mqtt_exec_logging_level) mqtt_exec_handler.setFormatter(exec_formatter) exec_logger.addHandler(mqtt_exec_handler) - msg+=colored(f"\n\u2611 Publishes execution as {MQTT_LOGGING_CONFIG['exec_topic']} topic on the {MQTT_LOGGING_CONFIG['hostname']} broker", 'blue') + msg += colored(f"\n\u2611 Publishes execution as {MQTT_LOGGING_CONFIG['exec_topic']} topic on the " + f"{MQTT_LOGGING_CONFIG['hostname']} broker", 'blue') except Exception as e: msg += colored(f'\nWarning: Unable to connect to exec topic on broker\n{e}', 'yellow') mqtt = False @@ -89,14 +92,17 @@ def setup_loggers(mqtt=True): if mqtt: mqtt_settings = MQTT_LOGGING_CONFIG.copy() - [mqtt_settings.pop(i) for i in ['client_id', 'exec_topic', 'data_topic', 'soh_topic']] + mqtt_data_logging_level = mqtt_settings.pop('data_logging_level', logging.INFO) + [mqtt_settings.pop(i) for i in ['client_id', 'exec_topic', 'data_topic', 'soh_topic', 'exec_logging_level', + 'soh_logging_level']] mqtt_settings.update({'topic': MQTT_LOGGING_CONFIG['data_topic']}) try: mqtt_data_handler = MQTTHandler(**mqtt_settings) - mqtt_data_handler.setLevel(DATA_LOGGING_CONFIG['logging_level']) + mqtt_data_handler.setLevel(MQTT_LOGGING_CONFIG['data_logging_level']) mqtt_data_handler.setFormatter(data_formatter) data_logger.addHandler(mqtt_data_handler) - msg += colored(f"\n\u2611 Publishes data as {MQTT_LOGGING_CONFIG['data_topic']} topic on the {MQTT_LOGGING_CONFIG['hostname']} broker", 'blue') + msg += colored(f"\n\u2611 Publishes data as {MQTT_LOGGING_CONFIG['data_topic']} topic on the " + f"{MQTT_LOGGING_CONFIG['hostname']} broker", 'blue') except Exception as e: msg += colored(f'\nWarning: Unable to connect to data topic on broker\n{e}', 'yellow') mqtt = False @@ -118,12 +124,12 @@ def init_logging(exec_logger, data_logger, exec_logging_level, log_path, data_lo exec_logger.info('*** NEW SESSION STARTING ***') exec_logger.info('****************************') exec_logger.info('') - exec_logger.debug('Logging level: %s' % exec_logging_level) + exec_logger.debug(f'Logging level: {exec_logging_level}') try: st = statvfs('.') available_space = st.f_bavail * st.f_frsize / 1024 / 1024 exec_logger.info(f'Remaining disk space : {available_space:.1f} MB') - except Exception as e: + except Exception as e: # noqa exec_logger.debug('Unable to get remaining disk space: {e}') exec_logger.info('Saving data log to ' + data_log_filename) config_dict = {'execution logging configuration': json.dumps(EXEC_LOGGING_CONFIG, indent=4), diff --git a/mqtt_interface.py b/mqtt_interface.py deleted file mode 100644 index f90a6ae0df2f3bb583e47541c03127f5c74aae81..0000000000000000000000000000000000000000 --- a/mqtt_interface.py +++ /dev/null @@ -1,53 +0,0 @@ -import paho.mqtt.client as mqtt -from config import MQTT_CONTROL_CONFIG, CONTROL_CONFIG, OHMPI_CONFIG -import time -from queue import Queue -import zmq - -ctrl_queue = Queue() - -def on_message(client, userdata, message): - global socket - - # Send the command - print(f'Sending command {message.payload.decode("utf-8")}') - socket.send(message.payload) - - # Get the reply - reply = socket.recv() - print(f'Received reply {message.payload.decode("utf-8")}: {reply}') - - -mqtt_client = mqtt.Client(f'ohmpi_{OHMPI_CONFIG["id"]}_listener', clean_session=False) # create new instance -print('connecting command listener to broker') -trials = 0 -trials_max = 10 -broker_connected = False -while trials < trials_max: - try: - mqtt_client.username_pw_set(MQTT_CONTROL_CONFIG['auth'].get('username'), - MQTT_CONTROL_CONFIG['auth']['password']) - mqtt_client.connect(MQTT_CONTROL_CONFIG['hostname']) - trials = trials_max - broker_connected = True - except: - print('trying again...') - time.sleep(2) - trials += 1 -if broker_connected: - print('Subscribing to topic', MQTT_CONTROL_CONFIG['ctrl_topic']) - mqtt_client.subscribe(MQTT_CONTROL_CONFIG['ctrl_topic'], MQTT_CONTROL_CONFIG['qos']) - mqtt_client.on_message = on_message - mqtt_client.loop_start() - - context = zmq.Context() - # Socket to talk to server - print("Connecting to ohmpi control server") - socket = context.socket(zmq.REQ) - socket.connect(f'tcp://localhost:{CONTROL_CONFIG["tcp_port"]}') - - while True: - time.sleep(.1) -else: - print("Unable to connect to control broker") - exit(1) diff --git a/ohmpi.py b/ohmpi.py index d3cacfcb117b224fc56cb3f83b740677fab0b1bb..5f6630a1651b54c3d16b327c1e1bdcda9a8f7ce2 100644 --- a/ohmpi.py +++ b/ohmpi.py @@ -15,7 +15,6 @@ from copy import deepcopy import numpy as np import csv import time -from io import StringIO from datetime import datetime from termcolor import colored import threading @@ -35,7 +34,8 @@ try: import digitalio # noqa from digitalio import Direction # noqa from gpiozero import CPUTemperature # noqa - import minimalmodbus # noqa + import minimalmodbus # noqa + arm64_imports = True except ImportError as error: if EXEC_LOGGING_CONFIG['logging_level'] == DEBUG: @@ -130,13 +130,13 @@ class OhmPi(object): # current injection module if self.idps: self.DPS = minimalmodbus.Instrument(port='/dev/ttyUSB0', slaveaddress=1) # port name, address (decimal) - self.DPS.serial.baudrate = 9600 # Baud rate 9600 as listed in doc - self.DPS.serial.bytesize = 8 # - self.DPS.serial.timeout = 1 # greater than 0.5 for it to work - self.DPS.debug = False # - self.DPS.serial.parity = 'N' # No parity - self.DPS.mode = minimalmodbus.MODE_RTU # RTU mode - self.DPS.write_register(0x0001, 40, 0) # max current allowed (36 mA for relays) + self.DPS.serial.baudrate = 9600 # Baud rate 9600 as listed in doc + self.DPS.serial.bytesize = 8 # + self.DPS.serial.timeout = 1 # greater than 0.5 for it to work + self.DPS.debug = False # + self.DPS.serial.parity = 'N' # No parity + self.DPS.mode = minimalmodbus.MODE_RTU # RTU mode + self.DPS.write_register(0x0001, 40, 0) # max current allowed (36 mA for relays) # (last number) 0 is for mA, 3 is for A # injection courant and measure (TODO check if it works, otherwise back in run_measurement()) @@ -157,7 +157,7 @@ class OhmPi(object): f" on {MQTT_CONTROL_CONFIG['hostname']} broker") def connect_mqtt() -> mqtt_client: - def on_connect(client, userdata, flags, rc): + def on_connect(mqttclient, userdata, flags, rc): if rc == 0: self.exec_logger.debug(f"Successfully connected to control broker:" f" {MQTT_CONTROL_CONFIG['hostname']}") @@ -170,6 +170,7 @@ class OhmPi(object): client.on_connect = on_connect client.connect(MQTT_CONTROL_CONFIG['hostname'], MQTT_CONTROL_CONFIG['port']) return client + try: self.exec_logger.debug(f"Connecting to control broker: {MQTT_CONTROL_CONFIG['hostname']}") self.controller = connect_mqtt() @@ -209,6 +210,7 @@ class OhmPi(object): Parameters ---------- + cmd_id filename : str filename to save the last measurement dataframe last_measurement : dict @@ -305,8 +307,8 @@ class OhmPi(object): time.sleep(best_tx_injtime) # inject for given tx time # autogain - self.ads_current = ads.ADS1115(self.i2c, gain=2/3, data_rate=860, address=self.ads_current_address) - self.ads_voltage = ads.ADS1115(self.i2c, gain=2/3, data_rate=860, address=self.ads_voltage_address) + self.ads_current = ads.ADS1115(self.i2c, gain=2 / 3, data_rate=860, address=self.ads_current_address) + self.ads_voltage = ads.ADS1115(self.i2c, gain=2 / 3, data_rate=860, address=self.ads_voltage_address) # print('current P0', AnalogIn(self.ads_current, ads.P0).voltage) # print('voltage P0', AnalogIn(self.ads_voltage, ads.P0).voltage) # print('voltage P2', AnalogIn(self.ads_voltage, ads.P2).voltage) @@ -320,8 +322,8 @@ class OhmPi(object): # we measure the voltage on both A0 and A2 to guess the polarity I = AnalogIn(self.ads_current, ads.P0).voltage * 1000. / 50 / self.r_shunt # noqa measure current - U0 = AnalogIn(self.ads_voltage, ads.P0).voltage * 1000. # measure voltage - U2 = AnalogIn(self.ads_voltage, ads.P2).voltage * 1000. + U0 = AnalogIn(self.ads_voltage, ads.P0).voltage * 1000. # noqa measure voltage + U2 = AnalogIn(self.ads_voltage, ads.P2).voltage * 1000. # noqa # print('I (mV)', I*50*self.r_shunt) # print('I (mA)', I) # print('U0 (mV)', U0) @@ -337,7 +339,7 @@ class OhmPi(object): # compute constant c = vmn / I - Rab = (volt * 1000.) / I + Rab = (volt * 1000.) / I # noqa self.exec_logger.debug(f'Rab = {Rab:.2f} Ohms') @@ -444,6 +446,7 @@ class OhmPi(object): Parameters ---------- + cmd_id filename : str Path of the .csv or .txt file with A, B, M and N electrodes. Electrode index start at 1. @@ -546,8 +549,6 @@ 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}") - # f"Unable to execute {cmd}({str(args) + ', ' if args is not None else ''}" - # f"{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}') @@ -699,7 +700,7 @@ class OhmPi(object): self.pin1.value = False # one stack = 2 half-cycles (one positive, one negative) - pinMN = 0 if polarity > 0 else 2 + pinMN = 0 if polarity > 0 else 2 # noqa # sampling for each stack at the end of the injection sampling_interval = 10 # ms @@ -752,7 +753,7 @@ class OhmPi(object): end_delay = time.time() # truncate the meas array if we didn't fill the last samples - meas = meas[:k+1] + meas = meas[:k + 1] # measurement of current i and voltage u during off time measpp = np.zeros((meas.shape[0], 3)) * np.nan @@ -779,13 +780,13 @@ class OhmPi(object): end_delay = time.time() # truncate the meas array if we didn't fill the last samples - measpp = measpp[:k+1] + measpp = measpp[:k + 1] # we alternate on which ADS1115 pin we measure because of sign of voltage if pinMN == 0: - pinMN = 2 + pinMN = 2 # noqa else: - pinMN = 0 + pinMN = 0 # noqa # store data for full wave form fulldata.append(meas) @@ -799,8 +800,8 @@ class OhmPi(object): # take average from the samples per stack, then sum them all # average for the last third of the stacked values # is done outside the loop - sum_i = sum_i + (np.mean(meas[-int(meas.shape[0]//3):, 0])) - vmn1 = np.mean(meas[-int(meas.shape[0]//3), 1]) + sum_i = sum_i + (np.mean(meas[-int(meas.shape[0] // 3):, 0])) + vmn1 = np.mean(meas[-int(meas.shape[0] // 3), 1]) if (n % 2) == 0: sum_vmn = sum_vmn - vmn1 sum_ps = sum_ps + vmn1 @@ -872,119 +873,6 @@ class OhmPi(object): return d - def run_multiple_sequences(self, sequence_delay=None, nb_meas=None, **kwargs): - """Runs multiple sequences in a separate thread for monitoring mode. - Can be stopped by 'OhmPi.interrupt()'. - Additional arguments are passed to run_measurement(). - - Parameters - ---------- - sequence_delay : int, optional - Number of seconds at which the sequence must be started from each others. - nb_meas : int, optional - Number of time the sequence must be repeated. - kwargs : dict, optional - See help(k.run_measurement) for more info. - """ - # self.run = True - if sequence_delay is None: - sequence_delay = self.settings['sequence_delay'] - sequence_delay = int(sequence_delay) - if nb_meas is None: - nb_meas = self.settings['nb_meas'] - self.status = 'running' - self.exec_logger.debug(f'Status: {self.status}') - self.exec_logger.debug(f'Measuring sequence: {self.sequence}') - - def func(): - for g in range(0, nb_meas): # for time-lapse monitoring - if self.status == 'stopping': - self.exec_logger.warning('Data acquisition interrupted') - 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: - time.sleep(dt) # waiting for next measurement (time-lapse) - self.status = 'idle' - self.thread = threading.Thread(target=func) - self.thread.start() - - def run_sequence(self, **kwargs): - """Runs sequence synchronously (=blocking on main thread). - Additional arguments are passed to run_measurement(). - """ - self.status = 'running' - self.exec_logger.debug(f'Status: {self.status}') - self.exec_logger.debug(f'Measuring sequence: {self.sequence}') - t0 = time.time() - - # create filename with timestamp - filename = self.settings["export_path"].replace('.csv', - f'_{datetime.now().strftime("%Y%m%dT%H%M%S")}.csv') - self.exec_logger.debug(f'Saving to {filename}') - - # make sure all multiplexer are off - self.reset_mux() - - # measure all quadrupole of the sequence - if self.sequence is None: - n = 1 - else: - n = self.sequence.shape[0] - for i in range(0, n): - if self.sequence is None: - quad = np.array([0, 0, 0, 0]) - else: - quad = self.sequence[i, :] # quadrupole - if self.status == 'stopping': - break - - # call the switch_mux function to switch to the right electrodes - self.switch_mux_on(quad) - - # run a measurement - if self.on_pi: - acquired_data = self.run_measurement(quad, **kwargs) - else: # for testing, generate random data - acquired_data = { - 'A': [quad[0]], 'B': [quad[1]], 'M': [quad[2]], 'N': [quad[3]], - 'R [ohm]': np.abs(np.random.randn(1)) - } - - # switch mux off - self.switch_mux_off(quad) - - # add command_id in dataset - # acquired_data.update({'cmd_id': cmd_id}) // in run_measurement() - # log data to the data logger - # self.data_logger.info(f'{acquired_data}') // in run_measurement() - # 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.status = 'idle' - - def run_sequence_async(self, cmd_id=None, **kwargs): - """Runs the sequence in a separate thread. Can be stopped by 'OhmPi.interrupt()'. - Additional arguments are passed to run_measurement(). - - Parameters - ---------- - cmd_id: - """ - - def func(): - self.run_sequence(**kwargs) - - self.thread = threading.Thread(target=func) - self.thread.start() - self.status = 'idle' - def run_multiple_sequences(self, cmd_id=None, sequence_delay=None, nb_meas=None, **kwargs): """Runs multiple sequences in a separate thread for monitoring mode. Can be stopped by 'OhmPi.interrupt()'. @@ -1026,6 +914,7 @@ class OhmPi(object): if nb_meas > 1: time.sleep(dt) # waiting for next measurement (time-lapse) self.status = 'idle' + self.thread = threading.Thread(target=func) self.thread.start() @@ -1077,7 +966,7 @@ class OhmPi(object): # add command_id in dataset acquired_data.update({'cmd_id': cmd_id}) # log data to the data logger - # self.data_logger.info(f'{acquired_data}') # already in run_measurement() + # self.data_logger.info(f'{acquired_data}') # 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}') @@ -1147,8 +1036,8 @@ class OhmPi(object): # 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' + f'V: {voltage :>10.3f} mV, ' \ + f'R: {resist :>10.3f} kOhm' self.exec_logger.debug(msg) @@ -1169,96 +1058,11 @@ class OhmPi(object): else: pass self.status = 'idle' + # # # TODO if interrupted, we would need to restore the values # # TODO or we offer the possibility in 'run_measurement' to have rs_check each time? - def set_sequence(self, sequence=None): - try: - self.sequence = np.array(sequence) - #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 stop(self): - warnings.warn('This function is deprecated. Use interrupt instead.', DeprecationWarning) - self.interrupt() - - def _switch_mux(self, electrode_nr, state, role): - """Selects the right channel for the multiplexer cascade for a given electrode. - - Parameters - ---------- - electrode_nr : int - Electrode index to be switched on or off. - state : str - Either 'on' or 'off'. - role : str - Either 'A', 'B', 'M' or 'N', so we can assign it to a MUX board. - """ - if not self.use_mux or not self.on_pi: - if not self.on_pi: - self.exec_logger.warning('Cannot reset mux while in simulation mode...') - else: - self.exec_logger.warning('You cannot use the multiplexer because use_mux is set to False.' - ' Set use_mux to True to use the multiplexer...') - elif self.sequence is None: - self.exec_logger.warning('Unable to switch MUX without a sequence') - else: - # choose with MUX board - tca = adafruit_tca9548a.TCA9548A(self.i2c, self.board_addresses[role]) - - # find I2C address of the electrode and corresponding relay - # considering that one MCP23017 can cover 16 electrodes - i2c_address = 7 - (electrode_nr - 1) // 16 # quotient without rest of the division - relay_nr = electrode_nr - (electrode_nr // 16) * 16 + 1 - - if i2c_address is not None: - # select the MCP23017 of the selected MUX board - mcp2 = MCP23017(tca[i2c_address]) - mcp2.get_pin(relay_nr - 1).direction = digitalio.Direction.OUTPUT - - if state == 'on': - mcp2.get_pin(relay_nr - 1).value = True - else: - mcp2.get_pin(relay_nr - 1).value = False - - self.exec_logger.debug(f'Switching relay {relay_nr} ' - f'({str(hex(self.board_addresses[role]))}) {state} for electrode {electrode_nr}') - else: - self.exec_logger.warning(f'Unable to address electrode nr {electrode_nr}') - - def switch_mux_on(self, quadrupole): - """Switches on multiplexer relays for given quadrupole. - - Parameters - ---------- - quadrupole : list of 4 int - List of 4 integers representing the electrode numbers. - """ - roles = ['A', 'B', 'M', 'N'] - # another check to be sure A != B - if quadrupole[0] != quadrupole[1]: - for i in range(0, 4): - if quadrupole[i] > 0: - self._switch_mux(quadrupole[i], 'on', roles[i]) - else: - self.exec_logger.error('Not switching MUX : A == B -> short circuit risk detected!') - - def switch_mux_off(self, quadrupole): - """Switches off multiplexer relays for given quadrupole. - - Parameters - ---------- - quadrupole : list of 4 int - List of 4 integers representing the electrode numbers. - """ - roles = ['A', 'B', 'M', 'N'] - for i in range(0, 4): - if quadrupole[i] > 0: - self._switch_mux(quadrupole[i], 'off', roles[i]) def set_sequence(self, sequence=None, cmd_id=None): try: self.sequence = np.array(sequence).astype(int) @@ -1268,9 +1072,9 @@ class OhmPi(object): self.exec_logger.warning(f'Unable to set sequence: {e}') status = False - def stop(self, cmd_id=None): + def stop(self, **kwargs): warnings.warn('This function is deprecated. Use interrupt instead.', DeprecationWarning) - self.interrupt() + self.interrupt(**kwargs) def _switch_mux(self, electrode_nr, state, role): """Selects the right channel for the multiplexer cascade for a given electrode. @@ -1321,6 +1125,7 @@ class OhmPi(object): Parameters ---------- + cmd_id quadrupole : list of 4 int List of 4 integers representing the electrode numbers. """ @@ -1338,6 +1143,7 @@ class OhmPi(object): Parameters ---------- + cmd_id quadrupole : list of 4 int List of 4 integers representing the electrode numbers. """ @@ -1346,65 +1152,6 @@ class OhmPi(object): if quadrupole[i] > 0: self._switch_mux(quadrupole[i], 'off', roles[i]) - def reset_mux(self): - """Switches off all multiplexer relays.""" - if self.on_pi and self.use_mux: - roles = ['A', 'B', 'M', 'N'] - for i in range(0, 4): - for j in range(1, self.max_elec + 1): - self._switch_mux(j, 'off', roles[i]) - self.exec_logger.debug('All MUX switched off.') - elif not self.on_pi: - self.exec_logger.warning('Cannot reset mux while in simulation mode...') - else: - self.exec_logger.warning('You cannot use the multiplexer because use_mux is set to False.' - ' Set use_mux to True to use the multiplexer...') - - def _update_acquisition_settings(self, config): - warnings.warn('This function is deprecated, use update_settings() instead.', DeprecationWarning) - self.update_settings(config) - - def update_settings(self, config): - """Updates acquisition settings from a json file or dictionary. - Parameters can be: - - nb_electrodes (number of electrode used, if 4, no MUX needed) - - injection_duration (in seconds) - - nb_meas (total number of times the sequence will be run) - - sequence_delay (delay in second between each sequence run) - - nb_stack (number of stack for each quadrupole measurement) - - export_path (path where to export the data, timestamp will be added to filename) - - Parameters - ---------- - config : str, dict - Path to the .json settings file or dictionary of settings. - """ - status = False - if config is not None: - try: - if isinstance(config, dict): - self.settings.update(config) - else: - with open(config) as json_file: - dic = json.load(json_file) - self.settings.update(dic) - self.exec_logger.info('Acquisition parameters updated: ' + str(self.settings)) - status = True - except Exception as e: - self.exec_logger.warning('Unable to update settings.') - status = False - else: - self.exec_logger.warning('Settings are missing...') - return status - - # Properties - @property - def sequence(self): - """Gets sequence""" - if self._sequence is not None: - assert isinstance(self._sequence, np.ndarray) - return self._sequence - def reset_mux(self, cmd_id=None): """Switches off all multiplexer relays.""" if self.on_pi and self.use_mux: @@ -1421,9 +1168,9 @@ class OhmPi(object): def _update_acquisition_settings(self, config): warnings.warn('This function is deprecated, use update_settings() instead.', DeprecationWarning) - self.update_settings(config) + self.update_settings(settings=config) - def update_settings(self, config:str, cmd_id=None): + def update_settings(self, settings: str, cmd_id=None): """Updates acquisition settings from a json file or dictionary. Parameters can be: - nb_electrodes (number of electrode used, if 4, no MUX needed) @@ -1435,21 +1182,22 @@ class OhmPi(object): Parameters ---------- - config : str, dict + cmd_id + settings : str, dict Path to the .json settings file or dictionary of settings. """ status = False - if config is not None: + if settings is not None: try: - if isinstance(config, dict): - self.settings.update(config) + if isinstance(settings, dict): + self.settings.update(settings) else: - with open(config) as json_file: + with open(settings) as json_file: dic = json.load(json_file) self.settings.update(dic) self.exec_logger.debug('Acquisition parameters updated: ' + str(self.settings)) status = True - except Exception as e: + except Exception as e: # noqa self.exec_logger.warning('Unable to update settings.') status = False else: