planktoscope/flows/main.json

11161 lines
308 KiB
JSON
Raw Normal View History

2020-01-08 14:32:46 +01:00
[
{
2020-11-24 17:25:15 +01:00
"id": "eaae323a.31b3",
2020-09-15 17:33:49 +02:00
"type": "tab",
2020-11-24 17:25:15 +01:00
"label": "Home",
2020-09-15 17:33:49 +02:00
"disabled": false,
"info": ""
},
{
2020-11-24 17:25:15 +01:00
"id": "b771c342.49603",
"type": "tab",
"label": "Sample",
"disabled": false,
"info": ""
2020-09-15 17:33:49 +02:00
},
{
2020-11-24 17:25:15 +01:00
"id": "bccd1f23.87219",
"type": "tab",
"label": "Optic Configuration",
"disabled": false,
"info": ""
2020-01-08 14:32:46 +01:00
},
{
2020-11-24 17:25:15 +01:00
"id": "baa1e3d9.cb29d",
"type": "tab",
"label": "Fluidic Acquisition",
"disabled": false,
"info": ""
2020-07-14 18:22:31 +02:00
},
{
2020-11-24 17:25:15 +01:00
"id": "cb95299c.2817c8",
"type": "tab",
"label": "Segmentation",
"disabled": false,
"info": ""
2020-09-15 17:33:49 +02:00
},
2020-12-03 14:19:13 +01:00
{
"id": "c1660bc.e7ff7f8",
"type": "tab",
"label": "Gallery",
"disabled": false,
"info": ""
},
2020-11-27 11:30:57 +01:00
{
2020-12-03 16:29:12 +01:00
"id": "9daf9e2b.019fc",
2020-11-27 11:30:57 +01:00
"type": "tab",
2020-12-03 16:29:12 +01:00
"label": "Administration",
2020-11-27 11:30:57 +01:00
"disabled": false,
"info": ""
},
{
2020-12-03 16:29:12 +01:00
"id": "1371dec5.76e671",
"type": "tab",
2020-12-03 16:29:12 +01:00
"label": "Settings",
"disabled": false,
"info": ""
},
{
"id": "f21ba04.c26266",
"type": "tab",
"label": "WIFI Management",
2021-11-03 12:08:23 +01:00
"disabled": false,
"info": ""
},
2020-09-15 17:33:49 +02:00
{
2020-11-27 11:30:57 +01:00
"id": "9a22e67a.378818",
2020-11-24 17:25:15 +01:00
"type": "tab",
2020-11-27 11:30:57 +01:00
"label": "MQTT Receive",
2020-11-24 17:25:15 +01:00
"disabled": false,
"info": ""
2020-09-15 17:33:49 +02:00
},
2020-12-04 23:04:19 +01:00
{
"id": "1eaf21c8.f7a21e",
"type": "tab",
"label": "Hardware Settings",
"disabled": false,
"info": ""
},
2020-09-15 17:33:49 +02:00
{
2020-11-24 17:25:15 +01:00
"id": "1c24ad9c.bebec2",
2020-09-15 17:33:49 +02:00
"type": "subflow",
2020-11-24 17:25:15 +01:00
"name": "Config",
"info": "An input to this subflow will get the configuration to be saved to disk.\n\nOn startup, this node outputs the loaded configuration",
2020-09-15 17:33:49 +02:00
"category": "",
"in": [
{
2020-12-05 00:20:44 +01:00
"x": 220,
2020-12-04 23:04:19 +01:00
"y": 160,
2020-09-15 17:33:49 +02:00
"wires": [
{
2020-12-05 00:20:44 +01:00
"id": "3ad9835.08c937c"
2020-09-15 17:33:49 +02:00
}
]
}
],
"out": [
{
2020-12-05 00:20:44 +01:00
"x": 1040,
2020-12-04 23:04:19 +01:00
"y": 60,
2020-09-15 17:33:49 +02:00
"wires": [
{
2020-11-24 17:25:15 +01:00
"id": "ad541674.4791c8",
2020-09-15 17:33:49 +02:00
"port": 0
},
{
"id": "f439663c.8abd3",
"port": 0
2020-09-15 17:33:49 +02:00
}
]
2020-07-14 18:22:31 +02:00
}
],
2020-09-15 17:33:49 +02:00
"env": [],
"color": "#DDAA99"
2020-07-14 18:22:31 +02:00
},
2020-12-04 23:04:19 +01:00
{
"id": "4ed26b8b.253504",
"type": "subflow",
"name": "Hardware config",
"info": "",
"category": "",
"in": [
{
"x": 40,
"y": 200,
"wires": [
{
"id": "53d163be.47cf24"
}
]
}
],
"out": [
{
"x": 1040,
"y": 80,
"wires": [
{
"id": "ddb4b136.fc0b78",
"port": 0
}
]
}
],
"env": [],
"color": "#DDAA99"
},
2020-09-15 17:33:49 +02:00
{
2020-11-24 17:25:15 +01:00
"id": "833bc5bb.217ba8",
"type": "ui_group",
"name": "Preview",
"tab": "181bb236.1e94be",
"order": 1,
"disp": true,
2020-11-27 11:30:57 +01:00
"width": 18,
2020-11-24 17:25:15 +01:00
"collapse": false
2020-09-15 17:33:49 +02:00
},
{
2020-11-24 17:25:15 +01:00
"id": "3a6bb13f.c9703e",
"type": "ui_tab",
"name": "Home",
"icon": "home",
"order": 1,
"disabled": false,
"hidden": false
2020-09-15 17:33:49 +02:00
},
{
2020-11-24 17:25:15 +01:00
"id": "181bb236.1e94be",
"type": "ui_tab",
"name": "Optic Configuration",
"icon": "fa-eye",
"order": 3,
"disabled": false,
"hidden": false
2020-09-15 17:33:49 +02:00
},
{
2020-11-24 17:25:15 +01:00
"id": "c9194f02.9d5e9",
"type": "ui_tab",
"name": "Fluidic Acquisition",
"icon": "fa-flask",
"order": 4,
"disabled": false,
"hidden": false
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
},
{
2020-11-24 17:25:15 +01:00
"id": "8d16beb8.9b3fb",
"type": "ui_tab",
"name": "Segmentation",
"icon": "fa-crop",
"order": 5,
"disabled": false,
"hidden": false
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
},
2020-10-06 17:22:25 +02:00
{
2020-11-24 17:25:15 +01:00
"id": "d9cd733b.ab73d",
"type": "ui_tab",
"name": "System Monitoring",
"icon": "fa-thermometer-full",
2020-11-24 17:25:15 +01:00
"order": 7,
"disabled": false,
"hidden": false
2020-10-06 17:22:25 +02:00
},
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
{
2020-11-24 17:25:15 +01:00
"id": "4248342d.e55fac",
2020-09-15 17:33:49 +02:00
"type": "ui_group",
2020-11-24 17:25:15 +01:00
"name": "Optic Characterization",
"tab": "181bb236.1e94be",
"order": 4,
2020-09-15 17:33:49 +02:00
"disp": true,
"width": "6",
2020-09-15 17:33:49 +02:00
"collapse": false
},
{
2020-11-24 17:25:15 +01:00
"id": "858a0e3c.987fe",
2020-09-15 17:33:49 +02:00
"type": "ui_group",
2020-11-24 17:25:15 +01:00
"name": "Preview",
"tab": "c9194f02.9d5e9",
"order": 1,
2020-09-15 17:33:49 +02:00
"disp": true,
"width": 10,
2020-09-15 17:33:49 +02:00
"collapse": false
},
{
2020-11-24 17:25:15 +01:00
"id": "64903b47.4034e4",
2020-09-15 17:33:49 +02:00
"type": "ui_group",
2020-11-24 17:25:15 +01:00
"name": "Navigation",
"tab": "8d16beb8.9b3fb",
"order": 3,
2020-11-24 17:25:15 +01:00
"disp": false,
"width": "6",
2020-09-15 17:33:49 +02:00
"collapse": false
},
{
2020-11-24 17:25:15 +01:00
"id": "3da7da8f.179606",
2020-09-15 17:33:49 +02:00
"type": "ui_group",
2020-11-24 17:25:15 +01:00
"name": "Processor",
"tab": "d9cd733b.ab73d",
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"order": 1,
2020-09-15 17:33:49 +02:00
"disp": true,
2020-11-24 17:25:15 +01:00
"width": "6",
2020-09-15 17:33:49 +02:00
"collapse": false
},
{
2020-11-24 17:25:15 +01:00
"id": "cc8bc4eb.651868",
2020-09-15 17:33:49 +02:00
"type": "ui_base",
"theme": {
"name": "theme-dark",
"lightTheme": {
"default": "#0094CE",
2020-11-24 17:25:15 +01:00
"baseColor": "#5900ce",
2020-09-15 17:33:49 +02:00
"baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
2020-11-24 17:25:15 +01:00
"edited": false,
2020-09-15 17:33:49 +02:00
"reset": false
},
"darkTheme": {
"default": "#097479",
"baseColor": "#097479",
"baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
"edited": true,
"reset": false
},
"customTheme": {
"name": "Untitled Theme 1",
"default": "#4B7930",
"baseColor": "#4B7930",
"baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"
},
"themeState": {
"base-color": {
"default": "#097479",
"value": "#097479",
2020-11-24 17:25:15 +01:00
"edited": true
2020-09-15 17:33:49 +02:00
},
"page-titlebar-backgroundColor": {
"value": "#097479",
"edited": false
},
"page-backgroundColor": {
"value": "#111111",
"edited": false
},
"page-sidebar-backgroundColor": {
"value": "#333333",
"edited": false
},
"group-textColor": {
"value": "#0eb8c0",
"edited": false
},
"group-borderColor": {
"value": "#555555",
"edited": false
},
"group-backgroundColor": {
"value": "#333333",
"edited": false
},
"widget-textColor": {
"value": "#eeeeee",
"edited": false
},
"widget-backgroundColor": {
"value": "#097479",
"edited": false
},
"widget-borderColor": {
"value": "#333333",
"edited": false
},
"base-font": {
"value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"
}
},
"angularTheme": {
"primary": "indigo",
"accents": "blue",
"warn": "red",
"background": "grey",
"palette": "light"
2020-07-14 18:27:49 +02:00
}
2020-09-15 17:33:49 +02:00
},
"site": {
"name": "PlanktoScope",
2020-09-15 17:33:49 +02:00
"hideToolbar": "false",
"allowSwipe": "false",
2020-09-15 17:33:49 +02:00
"lockMenu": "false",
"allowTempTheme": "true",
"dateFormat": "Y-MM-DD",
2020-09-15 17:33:49 +02:00
"sizes": {
"sx": 55,
"sy": 55,
"gx": 4,
"gy": 4,
"cx": 4,
"cy": 4,
"px": 4,
"py": 4
2020-09-15 17:33:49 +02:00
}
}
2020-07-14 18:27:49 +02:00
},
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
{
2020-11-24 17:25:15 +01:00
"id": "36739a35.7cce36",
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"type": "ui_tab",
2020-11-24 17:25:15 +01:00
"name": "Gallery",
"icon": "fa-file-image-o",
2020-11-24 17:25:15 +01:00
"order": 6,
2020-12-03 14:19:13 +01:00
"disabled": false,
"hidden": false
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
},
{
2020-11-24 17:25:15 +01:00
"id": "c0ebfc57.42527",
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"type": "ui_group",
2020-11-24 17:25:15 +01:00
"name": "Group 1",
"tab": "36739a35.7cce36",
"order": 1,
"disp": false,
"width": "24",
"collapse": false
2020-11-24 17:25:15 +01:00
},
{
"id": "737ec584.2eea2c",
"type": "ui_tab",
"name": "Sample",
"icon": "fa-eyedropper",
"order": 2,
"disabled": false,
"hidden": false
},
{
"id": "6f97e7ae.270c48",
"type": "ui_group",
"name": "Group 1",
"tab": "3a6bb13f.c9703e",
"order": 1,
"disp": false,
"width": "4",
2020-10-06 17:22:25 +02:00
"collapse": false
},
{
2020-11-24 17:25:15 +01:00
"id": "3e1ba03d.f01d8",
2020-10-06 17:22:25 +02:00
"type": "ui_group",
2020-11-24 17:25:15 +01:00
"name": "Sample Identification",
"tab": "737ec584.2eea2c",
"order": 1,
2020-10-06 17:22:25 +02:00
"disp": true,
2021-01-04 11:48:36 +01:00
"width": "10",
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"collapse": false
},
{
2020-11-24 17:25:15 +01:00
"id": "4e0cd5ea.17e59c",
2020-10-06 17:22:25 +02:00
"type": "ui_group",
2020-11-24 17:25:15 +01:00
"name": "Group 2",
"tab": "3a6bb13f.c9703e",
"order": 2,
"disp": false,
"width": "4",
"collapse": false
},
{
"id": "ef590206.24f6",
"type": "ui_group",
"name": "Group 3",
"tab": "3a6bb13f.c9703e",
"order": 3,
"disp": false,
"width": "4",
"collapse": false
},
{
"id": "ae8f6620.073358",
"type": "ui_group",
"name": "Group 4",
"tab": "3a6bb13f.c9703e",
"order": 4,
"disp": false,
"width": "4",
"collapse": false
},
{
"id": "196518b2.4d53b7",
"type": "ui_group",
"name": "Group 5",
"tab": "3a6bb13f.c9703e",
"order": 5,
"disp": false,
"width": "4",
"collapse": false
},
{
"id": "777a7c33.fcd804",
"type": "ui_group",
"name": "Group 6",
"tab": "3a6bb13f.c9703e",
2020-10-06 17:22:25 +02:00
"order": 6,
2020-11-24 17:25:15 +01:00
"disp": false,
"width": "4",
"collapse": false
},
{
"id": "cef1e703.bcf3c8",
"type": "ui_group",
"name": "Sample Location",
2020-11-24 17:25:15 +01:00
"tab": "737ec584.2eea2c",
2021-12-03 02:51:25 +01:00
"order": 3,
2020-12-03 23:13:08 +01:00
"disp": false,
"width": "10",
2020-11-24 17:25:15 +01:00
"collapse": false
2020-10-06 17:22:25 +02:00
},
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
{
2020-11-24 17:25:15 +01:00
"id": "5517c651.b2f668",
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"type": "ui_group",
2020-11-24 17:25:15 +01:00
"name": "Validation",
"tab": "737ec584.2eea2c",
2021-12-03 02:51:25 +01:00
"order": 5,
2020-11-24 17:25:15 +01:00
"disp": false,
2020-11-27 11:30:57 +01:00
"width": 10,
2020-11-24 17:25:15 +01:00
"collapse": false
},
{
"id": "fbd92986.1028c8",
"type": "ui_group",
"name": "Focus Adjustment",
"tab": "181bb236.1e94be",
"order": 2,
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"disp": true,
"width": 4,
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"collapse": false
},
{
2020-11-24 17:25:15 +01:00
"id": "707d9797.c8e798",
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"type": "ui_group",
2020-11-24 17:25:15 +01:00
"name": "Fluidic Manual Manipulation",
"tab": "181bb236.1e94be",
2020-11-27 11:30:57 +01:00
"order": 5,
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"disp": true,
"width": 4,
2020-11-24 17:25:15 +01:00
"collapse": false
2020-10-06 17:22:25 +02:00
},
{
2020-11-24 17:25:15 +01:00
"id": "7a0b4877.a5d268",
"type": "ui_group",
"name": "Navigation",
2020-11-24 17:25:15 +01:00
"tab": "181bb236.1e94be",
2020-11-27 11:30:57 +01:00
"order": 6,
2020-11-24 17:25:15 +01:00
"disp": false,
"width": 4,
2020-11-24 17:25:15 +01:00
"collapse": false
2020-10-06 17:22:25 +02:00
},
{
2020-11-24 17:25:15 +01:00
"id": "404c301a.19c4e",
"type": "ui_group",
2020-11-24 17:25:15 +01:00
"name": "Fraction size",
"tab": "c9194f02.9d5e9",
"order": 2,
"disp": true,
"width": "10",
"collapse": false
},
{
"id": "4322c187.e73e5",
"type": "ui_group",
"name": "Acquisition",
"tab": "c9194f02.9d5e9",
"order": 3,
2020-11-24 17:25:15 +01:00
"disp": true,
"width": 10,
"collapse": false
},
{
"id": "b7919ae2.c01788",
"type": "ui_group",
"name": "Navigation",
"tab": "c9194f02.9d5e9",
"order": 6,
"disp": false,
"width": "10",
2020-11-24 17:25:15 +01:00
"collapse": false
},
{
"id": "b5d61bc7.54fe48",
"type": "ui_group",
2020-11-25 16:58:32 +01:00
"name": "Statistics",
2020-11-24 17:25:15 +01:00
"tab": "c9194f02.9d5e9",
"order": 4,
2020-11-25 16:58:32 +01:00
"disp": true,
2020-11-24 17:25:15 +01:00
"width": "10",
"collapse": false
},
{
"id": "1be83144.4fe4bf",
"type": "ui_group",
"name": "Danger Zone (DO NOT TOUCH HERE UNLESS YOU KNOW WHAT YOU ARE DOING)",
"tab": "2489e51c.eed77a",
"order": 2,
"disp": true,
"width": "12",
2020-12-02 21:58:22 +01:00
"collapse": true
2020-11-24 17:25:15 +01:00
},
{
"id": "3ca00bf9.e5cac4",
"type": "ui_group",
"name": "Navigation",
"tab": "d9cd733b.ab73d",
"order": 7,
2020-12-02 21:58:22 +01:00
"disp": true,
"width": 6,
"collapse": false
},
{
2020-11-24 17:25:15 +01:00
"id": "b5ba3f26.2e722",
"type": "ui_group",
"name": "CPU Temperature",
"tab": "d9cd733b.ab73d",
"order": 2,
2020-11-24 17:25:15 +01:00
"disp": true,
"width": "6",
"collapse": false
},
{
2020-11-24 17:25:15 +01:00
"id": "806d69c8.67fc58",
"type": "ui_group",
"name": "Memory",
"tab": "d9cd733b.ab73d",
"order": 3,
"disp": true,
"width": "6",
"collapse": false
},
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
{
2020-11-24 17:25:15 +01:00
"id": "405183bc.d8991c",
"type": "ui_group",
"name": "Disk Usage",
"tab": "d9cd733b.ab73d",
"order": 4,
"disp": true,
"width": "6",
"collapse": false
2020-09-15 17:33:49 +02:00
},
{
2020-11-24 17:25:15 +01:00
"id": "8dc3722c.06efa8",
"type": "mqtt-broker",
2020-09-15 17:33:49 +02:00
"name": "",
2020-11-24 17:25:15 +01:00
"broker": "0.0.0.0",
"port": "1883",
"clientid": "Client_node",
"usetls": false,
"compatmode": false,
"protocolVersion": 4,
2020-11-24 17:25:15 +01:00
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"willTopic": "",
"willQos": "0",
"willPayload": ""
},
{
"id": "abeb6dad.635a2",
"type": "ui_group",
"name": "Control",
"tab": "8d16beb8.9b3fb",
"order": 1,
"disp": true,
"width": 10,
2020-11-24 17:25:15 +01:00
"collapse": false
},
{
"id": "cf5d9f0e.d57e7",
"type": "ui_group",
"name": "Net Metadata",
2020-11-24 17:25:15 +01:00
"tab": "737ec584.2eea2c",
2021-12-03 02:51:25 +01:00
"order": 4,
2020-12-03 23:13:08 +01:00
"disp": false,
"width": "10",
2020-11-24 17:25:15 +01:00
"collapse": false
},
{
"id": "ce9e278.781eed8",
"type": "ui_group",
"name": "Information",
"tab": "d9cd733b.ab73d",
"order": 5,
2020-11-24 17:25:15 +01:00
"disp": true,
2020-12-02 21:58:22 +01:00
"width": 6,
2020-11-24 17:25:15 +01:00
"collapse": false
},
{
2020-11-25 16:58:32 +01:00
"id": "70de8209.68416c",
2020-11-24 17:25:15 +01:00
"type": "ui_group",
2020-11-25 16:58:32 +01:00
"name": "Status",
"tab": "c9194f02.9d5e9",
"order": 5,
2020-11-24 17:25:15 +01:00
"disp": true,
2020-11-25 16:58:32 +01:00
"width": 10,
"collapse": false
2020-11-24 17:25:15 +01:00
},
{
2020-11-25 16:58:32 +01:00
"id": "46be9c86.dea684",
"type": "ui_group",
"name": "Status",
"tab": "8d16beb8.9b3fb",
"order": 2,
2020-11-25 16:58:32 +01:00
"disp": true,
"width": 10,
2020-11-25 16:58:32 +01:00
"collapse": false
},
{
2020-11-27 11:30:57 +01:00
"id": "8c38a81e.9897a8",
"type": "ui_group",
"name": "Camera Settings",
"tab": "181bb236.1e94be",
"order": 3,
"disp": true,
"width": 6,
2020-11-27 11:30:57 +01:00
"collapse": false
},
2020-11-25 16:58:32 +01:00
{
"id": "52d1b77.28369c8",
"type": "ui_group",
"name": "USB Backup",
"tab": "d9cd733b.ab73d",
"order": 6,
"disp": true,
"width": "6",
2020-12-02 21:58:22 +01:00
"collapse": true
},
{
"id": "a7d64879.38298",
"type": "ui_group",
"name": "Python Log",
"tab": "2489e51c.eed77a",
"order": 1,
"disp": true,
"width": "12",
2020-12-02 21:58:22 +01:00
"collapse": true
},
2020-11-30 01:52:15 +01:00
{
"id": "9e409235.73cd7",
"type": "ui_group",
"name": "Add a new network",
"tab": "1b49ae0f.602d6a",
"order": 2,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "919923a3.d10868",
"type": "ui_group",
"name": "Current Connection",
"tab": "1b49ae0f.602d6a",
"order": 1,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "1b49ae0f.602d6a",
"type": "ui_tab",
"name": "Wifi",
"icon": "wifi",
"order": 8,
"disabled": false,
"hidden": false
},
{
"id": "2489e51c.eed77a",
"type": "ui_tab",
"name": "Administration",
"icon": "dashboard",
"order": 9,
"disabled": false,
"hidden": false
},
2020-12-04 23:04:19 +01:00
{
"id": "b0fb559a.6966a8",
"type": "ui_tab",
"name": "Hardware Settings",
"icon": "fa-cogs",
"disabled": false,
"hidden": false
},
{
2021-09-06 18:39:52 +02:00
"id": "6be36295.0ab324",
"type": "ui_group",
"name": "Settings",
"tab": "b0fb559a.6966a8",
"order": 1,
"disp": false,
"width": "6",
"collapse": false
},
2021-01-04 11:48:36 +01:00
{
2021-01-15 11:48:27 +01:00
"id": "fc5e4e6f.5b1c8",
"type": "ui_group",
"name": "GPS Status",
"tab": "737ec584.2eea2c",
2021-12-03 02:51:25 +01:00
"order": 6,
2021-01-15 11:48:27 +01:00
"disp": true,
"width": 10,
"collapse": false
},
{
2021-09-06 18:39:52 +02:00
"id": "f3ca28ef.4df0a8",
"type": "ui_group",
"name": "Shutdown",
"tab": "3a6bb13f.c9703e",
"order": 7,
"disp": true,
"width": 4,
"collapse": false
},
{
"id": "3dfd8a69.69ed56",
2021-01-04 11:48:36 +01:00
"type": "ui_spacer",
"name": "spacer",
"group": "3e1ba03d.f01d8",
"order": 5,
"width": 10,
"height": 1
},
2021-09-06 18:39:52 +02:00
{
"id": "cfe2288f.a8862",
"type": "ui_group",
"name": "GPS Status",
"tab": "d9cd733b.ab73d",
"order": 8,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "72a9216.2ff48e",
"type": "ui_spacer",
"name": "spacer",
"group": "4322c187.e73e5",
"order": 8,
"width": 10,
"height": 1
},
{
"id": "1b664927.c91d1f",
"type": "ui_spacer",
"name": "spacer",
"group": "b5d61bc7.54fe48",
"order": 2,
"width": 2,
"height": 1
},
{
"id": "1a663b6a.ab5805",
"type": "ui_spacer",
"name": "spacer",
"group": "b5d61bc7.54fe48",
"order": 4,
"width": 2,
"height": 1
},
{
"id": "8eeed8d8.ad9098",
"type": "ui_spacer",
"name": "spacer",
"group": "b5d61bc7.54fe48",
"order": 5,
"width": 2,
"height": 1
},
{
"id": "be1c5ce7.004e",
"type": "ui_spacer",
"name": "spacer",
"group": "b5d61bc7.54fe48",
"order": 6,
"width": 2,
"height": 1
},
{
"id": "c2fe38e9.555e3",
"type": "ui_spacer",
"name": "spacer",
"group": "b5d61bc7.54fe48",
"order": 7,
"width": 2,
"height": 1
},
{
"id": "89bd18b7.c779a8",
"type": "ui_spacer",
"name": "spacer",
"group": "b5d61bc7.54fe48",
"order": 8,
"width": 2,
"height": 1
},
{
"id": "3cbbfa5d.efa636",
"type": "ui_spacer",
"name": "spacer",
"group": "b7919ae2.c01788",
"order": 2,
"width": 5,
"height": 1
},
{
"id": "97148e8d8e298f1a",
"type": "ui_spacer",
"z": "cb95299c.2817c8",
"name": "spacer",
"group": "abeb6dad.635a2",
"order": 7,
"width": 10,
"height": 1
},
{
"id": "3b2eccc574e6a9ae",
"type": "ui_spacer",
"z": "cb95299c.2817c8",
"name": "spacer",
"group": "abeb6dad.635a2",
"order": 11,
"width": 1,
"height": 1
},
{
"id": "bbd6431d97c86f97",
"type": "ui_spacer",
"z": "cb95299c.2817c8",
"name": "spacer",
"group": "46be9c86.dea684",
"order": 4,
"width": 3,
"height": 1
},
{
"id": "5f98f5f140f0ecde",
"type": "ui_spacer",
"z": "cb95299c.2817c8",
"name": "spacer",
"group": "46be9c86.dea684",
"order": 6,
"width": 3,
"height": 1
},
{
2021-12-03 02:51:25 +01:00
"id": "7bc0a4c416e4545c",
"type": "ui_group",
"name": "Culture Date and Time",
"tab": "737ec584.2eea2c",
"order": 2,
"disp": true,
"width": "10",
"collapse": false,
"className": ""
},
{
"id": "6c31ad948a9d62fd",
"type": "ui_spacer",
"name": "spacer",
"group": "4248342d.e55fac",
"order": 2,
"width": 1,
"height": 1
},
2021-12-01 13:22:14 +01:00
{
"id": "9b6dc1eeb6aacf25",
"type": "ui_spacer",
"z": "9daf9e2b.019fc",
"name": "spacer",
"group": "1be83144.4fe4bf",
"order": 5,
"width": 12,
"height": 1
},
{
"id": "39d7981eebaf1b17",
"type": "ui_spacer",
"z": "9daf9e2b.019fc",
"name": "spacer",
"group": "1be83144.4fe4bf",
"order": 6,
"width": 12,
"height": 1
},
{
"id": "603f43d66f25966f",
"type": "ui_spacer",
"z": "9daf9e2b.019fc",
"name": "spacer",
"group": "1be83144.4fe4bf",
"order": 7,
"width": 4,
"height": 1
},
{
"id": "02a0c4c401619cb8",
"type": "ui_spacer",
"z": "9daf9e2b.019fc",
"name": "spacer",
"group": "1be83144.4fe4bf",
"order": 9,
"width": 4,
"height": 1
},
{
"id": "1b7e5b6e265fe27d",
"type": "ui_spacer",
"z": "9daf9e2b.019fc",
"name": "spacer",
"group": "1be83144.4fe4bf",
"order": 11,
"width": 8,
"height": 1
},
{
"id": "d9622026ef2d9e0c",
"type": "ui_spacer",
"z": "9daf9e2b.019fc",
"name": "spacer",
"group": "1be83144.4fe4bf",
"order": 14,
"width": 8,
"height": 1
},
2020-11-24 17:25:15 +01:00
{
2021-12-03 02:34:14 +01:00
"id": "6465bdd5.15eb8c",
"type": "file in",
"z": "1c24ad9c.bebec2",
2020-11-24 17:25:15 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"filename": "/home/pi/PlanktoScope/config.json",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 480,
"y": 60,
2020-11-24 17:25:15 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"15ceb135.6628bf"
]
],
"info": "# PlanktoScope Help\nThis Node will read the content of the file named **config.txt** containing all the input placeholders.\n"
2020-11-24 17:25:15 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "15ceb135.6628bf",
"type": "json",
"z": "1c24ad9c.bebec2",
"name": "config.json",
"property": "payload",
"action": "",
"pretty": false,
"x": 730,
"y": 60,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"ad541674.4791c8"
2020-11-24 17:25:15 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "7205d267.36adcc",
"type": "file",
"z": "1c24ad9c.bebec2",
2020-11-24 17:25:15 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"filename": "/home/pi/PlanktoScope/config.json",
"appendNewline": true,
"createDir": true,
"overwriteFile": "true",
"encoding": "none",
"x": 990,
"y": 160,
2020-11-24 17:25:15 +01:00
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "2e6ddf51.c0dba",
"type": "json",
"z": "1c24ad9c.bebec2",
"name": "config.json",
"property": "payload",
"action": "str",
"pretty": true,
"x": 730,
"y": 160,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"7205d267.36adcc"
]
2020-11-24 17:25:15 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "ad541674.4791c8",
"type": "function",
"z": "1c24ad9c.bebec2",
"name": "Global Set",
"func": "global.set(\"config_keys\", Object.keys(msg.payload));\n\nfor (const key in msg.payload) {\n global.set(key, msg.payload[key]);\n}\n\nreturn msg;",
2020-11-24 17:25:15 +01:00
"outputs": 1,
2021-12-03 02:34:14 +01:00
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 910,
"y": 60,
2020-11-24 17:25:15 +01:00
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "3e9a33c.141384c",
"type": "inject",
"z": "1c24ad9c.bebec2",
"name": "Load config",
"props": [
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payloadType": "str",
"x": 230,
"y": 60,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"6465bdd5.15eb8c"
]
2020-11-24 17:25:15 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "3ad9835.08c937c",
"type": "function",
"z": "1c24ad9c.bebec2",
"name": "get config payload",
"func": "keys = global.get(\"config_keys\")\n\nvar payload = {}\n\nkeys.forEach(function(item, index, array) {\n payload[item] = global.get(item);\n})\n\nreturn {\"payload\": payload};",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 410,
"y": 160,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"2e6ddf51.c0dba"
]
]
},
{
"id": "f439663c.8abd3",
"type": "ui_ui_control",
"z": "1c24ad9c.bebec2",
"name": "Connect Event",
"events": "connect",
"x": 900,
"y": 100,
"wires": [
2020-11-24 17:25:15 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "1f63860d.5f5efa",
"type": "file in",
"z": "4ed26b8b.253504",
2020-11-24 17:25:15 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"filename": "/home/pi/PlanktoScope/hardware.json",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 380,
"y": 80,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"8c1fda48.d7d828"
2020-11-24 17:25:15 +01:00
]
2021-12-03 02:34:14 +01:00
],
"info": "# PlanktoScope Help\nThis Node will read the content of the file named **config.txt** containing all the input placeholders.\n"
2020-11-24 17:25:15 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "8c1fda48.d7d828",
"type": "json",
"z": "4ed26b8b.253504",
"name": "Parse JSON",
"property": "payload",
"action": "",
"pretty": false,
"x": 650,
"y": 80,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"ddb4b136.fc0b78"
]
2020-11-24 17:25:15 +01:00
]
},
2020-07-14 18:22:31 +02:00
{
2021-12-03 02:34:14 +01:00
"id": "82099021.9ceb08",
"type": "file",
"z": "4ed26b8b.253504",
"name": "",
"filename": "/home/pi/PlanktoScope/hardware.json",
"appendNewline": true,
"createDir": true,
"overwriteFile": "true",
"encoding": "none",
"x": 780,
"y": 200,
2020-01-08 14:32:46 +01:00
"wires": [
2020-09-15 17:33:49 +02:00
[]
2020-01-08 14:32:46 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "bb0a8725.a1849",
"type": "json",
"z": "4ed26b8b.253504",
"name": "Create JSON",
2020-09-15 17:33:49 +02:00
"property": "payload",
2021-12-03 02:34:14 +01:00
"action": "str",
"pretty": true,
"x": 500,
"y": 200,
2020-01-08 14:32:46 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"82099021.9ceb08"
2020-01-08 14:32:46 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "f748d952.4b6bc8",
"type": "inject",
"z": "4ed26b8b.253504",
"name": "Load config",
"props": [
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"x": 110,
"y": 80,
2020-07-14 18:27:49 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"1f63860d.5f5efa"
2020-07-14 18:27:49 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "ddb4b136.fc0b78",
2020-11-24 17:25:15 +01:00
"type": "change",
2021-12-03 02:34:14 +01:00
"z": "4ed26b8b.253504",
"name": "",
2020-11-24 17:25:15 +01:00
"rules": [
{
"t": "set",
2021-12-03 02:34:14 +01:00
"p": "hardware_conf",
"pt": "global",
"to": "payload",
"tot": "msg"
2020-11-24 17:25:15 +01:00
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
2021-12-03 02:34:14 +01:00
"x": 870,
"y": 80,
2020-07-14 18:27:49 +02:00
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "53d163be.47cf24",
2020-11-24 17:25:15 +01:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "4ed26b8b.253504",
"name": "Update and retrieve hardware_conf",
"func": "// change global\nhardware_conf = global.get(\"hardware_conf\");\n\nif (msg.topic == \"process_pixel_fixed\" && msg.payload == 0){\n delete hardware_conf[msg.topic]\n delete msg.topic\n}\n\nif (msg.topic !== null && msg.topic !== undefined){\n hardware_conf[msg.topic] = msg.payload;\n global.set(\"hardware_conf\", hardware_conf);\n}\n\nreturn {\"payload\": hardware_conf};",
2020-11-24 17:25:15 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"libs": [],
"x": 240,
2021-12-01 13:30:23 +01:00
"y": 200,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"bb0a8725.a1849"
2020-11-24 17:25:15 +01:00
]
]
2020-07-14 18:22:31 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "4e78af2d.90be7",
"type": "ui_ui_control",
"z": "eaae323a.31b3",
"name": "",
"events": "change",
"x": 440,
"y": 160,
2020-07-14 18:22:31 +02:00
"wires": [
2021-12-03 02:34:14 +01:00
[]
2020-07-14 18:22:31 +02:00
]
},
{
2020-11-24 17:25:15 +01:00
"id": "7789839d.69b48c",
"type": "ui_template",
"z": "eaae323a.31b3",
"group": "4e0cd5ea.17e59c",
"name": "Optic Configuration",
"order": 1,
"width": 4,
2020-09-15 17:33:49 +02:00
"height": 4,
2020-12-05 00:20:44 +01:00
"format": "<md-button ng-click=\"send({payload: {tab:'Optic Configuration'}})\" style=\"height:100%;\">\n <i class=\"fa fa-eye fa-5x\"></i>\n <div style=\"margin-top:30px;\">Optic Configuration</div>\n</md-button>",
2020-11-24 17:25:15 +01:00
"storeOutMessages": true,
"fwdInMessages": true,
2020-12-05 00:20:44 +01:00
"resendOnRefresh": false,
2020-11-24 17:25:15 +01:00
"templateScope": "local",
"x": 130,
"y": 100,
2020-07-14 18:22:31 +02:00
"wires": [
2020-11-24 17:25:15 +01:00
[
2020-12-05 00:20:44 +01:00
"4e78af2d.90be7"
2020-11-24 17:25:15 +01:00
]
2020-07-14 18:22:31 +02:00
]
},
{
2020-11-24 17:25:15 +01:00
"id": "6ea6c306.f9c12c",
"type": "ui_template",
"z": "eaae323a.31b3",
"group": "ef590206.24f6",
"name": "Fluidic Acquisition",
"order": 1,
"width": 4,
"height": 4,
2020-12-05 00:20:44 +01:00
"format": "<md-button ng-click=\"send({payload: {tab:'Fluidic Acquisition'}})\" style=\"height:100%;\">\n <i class=\"fa fa-flask fa-5x\"></i>\n <div style=\"margin-top:30px;\">Fluidic Acquisition</div>\n</md-button>",
2020-11-24 17:25:15 +01:00
"storeOutMessages": true,
"fwdInMessages": true,
2020-12-05 00:20:44 +01:00
"resendOnRefresh": false,
2020-11-24 17:25:15 +01:00
"templateScope": "local",
"x": 130,
"y": 140,
2020-07-14 18:22:31 +02:00
"wires": [
[
2020-12-05 00:20:44 +01:00
"4e78af2d.90be7"
2020-07-14 18:22:31 +02:00
]
]
},
{
2020-11-24 17:25:15 +01:00
"id": "bb9eb153.9e36c",
"type": "ui_template",
"z": "eaae323a.31b3",
"group": "ae8f6620.073358",
"name": "Segmentation",
"order": 1,
"width": 4,
2020-09-15 17:33:49 +02:00
"height": 4,
2020-12-05 00:20:44 +01:00
"format": "<md-button ng-click=\"send({payload: {tab:'Segmentation'}})\" style=\"height:100%;\">\n <i class=\"fa fa-crop fa-5x\"></i>\n <div style=\"margin-top:30px;\">Segmentation</div>\n</md-button>",
2020-11-24 17:25:15 +01:00
"storeOutMessages": true,
"fwdInMessages": true,
2020-12-05 00:20:44 +01:00
"resendOnRefresh": false,
2020-11-24 17:25:15 +01:00
"templateScope": "local",
"x": 140,
"y": 180,
2020-07-14 18:22:31 +02:00
"wires": [
2020-11-24 17:25:15 +01:00
[
2020-12-05 00:20:44 +01:00
"4e78af2d.90be7"
2020-11-24 17:25:15 +01:00
]
2020-07-14 18:22:31 +02:00
]
},
{
2020-11-24 17:25:15 +01:00
"id": "eaf8ee7f.96f44",
"type": "ui_template",
"z": "eaae323a.31b3",
"group": "196518b2.4d53b7",
"name": "Gallery",
"order": 1,
"width": 4,
"height": 4,
2020-12-05 00:20:44 +01:00
"format": "<md-button ng-click=\"send({payload: {tab:'Gallery'}})\" style=\"height:100%;\">\n <i class=\"fa fa-file-image-o fa-5x\"></i>\n <div style=\"margin-top:30px;\">Gallery</div>\n</md-button>",
2020-11-24 17:25:15 +01:00
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": false,
2020-11-24 17:25:15 +01:00
"templateScope": "local",
"x": 160,
"y": 220,
2020-07-14 18:22:31 +02:00
"wires": [
[
2020-12-05 00:20:44 +01:00
"4e78af2d.90be7"
2020-07-14 18:22:31 +02:00
]
]
},
2020-11-24 17:25:15 +01:00
{
"id": "c1b1469.9650eb8",
"type": "ui_template",
"z": "eaae323a.31b3",
"group": "777a7c33.fcd804",
2020-12-04 23:04:19 +01:00
"name": "System Monitoring",
2020-11-24 17:25:15 +01:00
"order": 1,
"width": 4,
"height": 4,
"format": "<md-button ng-click=\"send({payload: {tab:'System Monitoring'}})\" style=\"height:100%;\">\n <i class=\"fa fa-thermometer-full fa-5x\"></i>\n <div style=\"margin-top:30px;\">System Monitoring</div>\n</md-button>",
2020-11-24 17:25:15 +01:00
"storeOutMessages": true,
"fwdInMessages": true,
2020-12-04 23:04:19 +01:00
"resendOnRefresh": false,
2020-11-24 17:25:15 +01:00
"templateScope": "local",
2020-12-04 23:04:19 +01:00
"x": 130,
2020-11-24 17:25:15 +01:00
"y": 260,
"wires": [
[
2020-12-05 00:20:44 +01:00
"4e78af2d.90be7"
2020-11-24 17:25:15 +01:00
]
]
},
{
"id": "abafd6e6.04a5f8",
"type": "ui_template",
2021-12-03 02:34:14 +01:00
"z": "eaae323a.31b3",
"group": "6f97e7ae.270c48",
"name": "Sample",
"order": 1,
"width": 4,
"height": 4,
"format": "<md-button ng-click=\"send({payload: {tab:'Sample'}})\" style=\"height:100%;\">\n <i class=\"fa fa-eyedropper fa-5x\"></i>\n <div style=\"margin-top:30px;\">Sample</div>\n <br>\n In doubt? Start Here!\n</md-button>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": false,
"templateScope": "local",
"x": 160,
"y": 60,
2020-01-08 14:32:46 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"4e78af2d.90be7"
2020-01-08 14:32:46 +01:00
]
2020-11-24 17:25:15 +01:00
]
2020-01-08 14:32:46 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "dab82064.26a8d",
"type": "ui_button",
"z": "eaae323a.31b3",
"name": "Unlock button",
"group": "f3ca28ef.4df0a8",
"order": 2,
2020-11-24 17:25:15 +01:00
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"passthru": false,
"label": "Unlock button",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "",
"payload": "shutdown",
"payloadType": "flow",
"topic": "",
"x": 120,
"y": 380,
2020-01-08 14:32:46 +01:00
"wires": [
2020-09-15 17:33:49 +02:00
[
2021-12-03 02:34:14 +01:00
"6c3a3b4.78cad44"
2020-09-15 17:33:49 +02:00
]
2020-01-08 14:32:46 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "1c658761.b852a1",
"type": "ui_toast",
"z": "eaae323a.31b3",
"position": "dialog",
"displayTime": "3",
"highlight": "",
"sendall": true,
"outputs": 1,
"ok": "YES PLEASE",
"cancel": "NO!",
"raw": false,
"topic": "Are you sure?",
"name": "Confirmation message",
"x": 800,
"y": 380,
2020-01-08 14:32:46 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"8f1b8e23.daafe"
2020-01-08 14:32:46 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "a48ff63f.db5e18",
"type": "inject",
"z": "eaae323a.31b3",
"name": "disabled",
"props": [
{
"p": "enabled",
"v": "false",
"vt": "bool"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"x": 140,
"y": 420,
2020-01-08 14:32:46 +01:00
"wires": [
2020-11-24 17:25:15 +01:00
[
2021-12-03 02:34:14 +01:00
"d58039a9.6e7928"
2020-11-24 17:25:15 +01:00
]
2020-01-08 14:32:46 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "b67a7147.65fcd8",
"type": "ui_button",
"z": "eaae323a.31b3",
"name": "Shutdown button",
"group": "f3ca28ef.4df0a8",
"order": 4,
2020-07-14 18:22:31 +02:00
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"passthru": false,
"label": "Shutdown",
"tooltip": "",
"color": "",
"bgcolor": "#AD1625",
"icon": "fa-power-off fa-2x",
"payload": "Do you want to turn the machine off now?",
"payloadType": "str",
"topic": "",
"x": 550,
"y": 380,
2020-01-08 14:32:46 +01:00
"wires": [
2020-11-24 17:25:15 +01:00
[
2021-12-03 02:34:14 +01:00
"1c658761.b852a1"
2020-11-24 17:25:15 +01:00
]
2020-01-08 14:32:46 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d58039a9.6e7928",
"type": "change",
"z": "eaae323a.31b3",
"name": "",
"rules": [
2020-11-24 17:25:15 +01:00
{
2021-12-03 02:34:14 +01:00
"t": "set",
"p": "shutdown",
"pt": "flow",
"to": "false",
"tot": "bool"
2020-11-24 17:25:15 +01:00
},
{
2021-12-03 02:34:14 +01:00
"t": "set",
"p": "enabled",
"pt": "msg",
"to": "shutdown",
"tot": "flow"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 320,
"y": 420,
2020-01-08 14:32:46 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"b67a7147.65fcd8"
2020-07-14 18:22:31 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6c3a3b4.78cad44",
2020-07-14 18:22:31 +02:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "eaae323a.31b3",
"name": "Toggle",
"func": "flow.set(\"shutdown\", !flow.get(\"shutdown\"))\nmsg.enabled = flow.get(\"shutdown\")\nreturn msg;",
2020-07-14 18:22:31 +02:00
"outputs": 1,
"noerr": 0,
2020-11-24 17:25:15 +01:00
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 290,
"y": 380,
2020-07-14 18:22:31 +02:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"b67a7147.65fcd8"
]
2020-07-14 18:22:31 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d1153ad6.40d738",
"type": "ui_template",
"z": "eaae323a.31b3",
"group": "f3ca28ef.4df0a8",
"name": "Shutdown information",
"order": 1,
"width": 0,
"height": 0,
"format": "<center>To prevent data corruption, please <b>always shutdown the machine</b> before unplugging the unit.\n<br><br>\nRemember to first unlock the shutdown button.\n</center>",
"storeOutMessages": true,
"fwdInMessages": false,
"resendOnRefresh": true,
"templateScope": "local",
"x": 120,
"y": 320,
2020-07-14 18:22:31 +02:00
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "e08cfcb8.2a67e8",
"type": "link out",
"z": "eaae323a.31b3",
"name": "Home shutdown button",
"links": [
"b81b990a.d4dca"
],
"x": 715,
"y": 500,
"wires": []
},
{
"id": "ce7087fc.dcc9e8",
2020-11-24 17:25:15 +01:00
"type": "ui_toast",
2021-12-03 02:34:14 +01:00
"z": "eaae323a.31b3",
2020-11-24 17:25:15 +01:00
"position": "dialog",
2021-12-03 02:34:14 +01:00
"displayTime": "10",
2020-11-24 17:25:15 +01:00
"highlight": "",
"sendall": true,
"outputs": 1,
"ok": "OK",
"cancel": "",
"raw": false,
2021-12-03 02:34:14 +01:00
"topic": "Turning off now!",
"name": "Shutdown message",
2021-12-03 02:51:25 +01:00
"x": 570,
2021-12-03 02:34:14 +01:00
"y": 540,
2020-07-14 18:22:31 +02:00
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "7d1626a0.deb85",
"type": "change",
"z": "eaae323a.31b3",
"name": "shutdown!",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "shutdown",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
2021-12-03 02:51:25 +01:00
"x": 300,
2021-12-03 02:34:14 +01:00
"y": 500,
2020-07-14 18:22:31 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"e08cfcb8.2a67e8"
2020-07-14 18:22:31 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "8f1b8e23.daafe",
"type": "switch",
"z": "eaae323a.31b3",
2020-07-14 18:22:31 +02:00
"name": "",
2021-12-03 02:34:14 +01:00
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "NO!",
"vt": "str"
},
{
"t": "else"
}
],
"checkall": "true",
"repair": false,
"outputs": 2,
"x": 150,
"y": 500,
2020-07-14 18:22:31 +02:00
"wires": [
2021-01-15 11:48:27 +01:00
[
2021-12-03 02:34:14 +01:00
"d58039a9.6e7928"
],
[
"7d1626a0.deb85",
"281a56c9.ec7902"
2021-01-15 11:48:27 +01:00
]
2020-11-24 17:25:15 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "281a56c9.ec7902",
"type": "change",
"z": "eaae323a.31b3",
"name": "Shutdown message",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "Please wait a minute before disconnecting the machine from its power supply!",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
2021-12-03 02:51:25 +01:00
"x": 330,
2021-12-03 02:34:14 +01:00
"y": 540,
2020-07-14 18:22:31 +02:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"ce7087fc.dcc9e8"
]
2020-07-14 18:22:31 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4557d689.a4fa88",
"type": "ui_text_input",
"z": "b771c342.49603",
"name": "sample_ship",
"label": "Name of the ship",
"tooltip": "",
"group": "3e1ba03d.f01d8",
"order": 2,
"width": 0,
"height": 0,
"passthru": true,
"mode": "text",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "sample_ship",
"sendOnBlur": true,
"className": "",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"x": 670,
"y": 80,
2020-07-14 18:22:31 +02:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"9f501f49.45645"
]
2020-07-14 18:22:31 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "fcfc31ae.af3af",
"type": "ui_dropdown",
"z": "b771c342.49603",
"name": "sample_sampling_gear",
"label": "Sampling gear*",
"tooltip": "",
"place": "Choose from list",
"group": "3e1ba03d.f01d8",
"order": 6,
"width": 0,
"height": 0,
"passthru": true,
"multiple": false,
"options": [
{
"label": "High Speed Net",
"value": "net_hsn",
"type": "str"
},
{
"label": "Tara Decknet",
"value": "net_decknet",
"type": "str"
},
{
"label": "Plankton net",
"value": "net",
"type": "str"
},
{
"label": "Niskin bottle 12L",
"value": "niskin_12L",
"type": "str"
},
{
"label": "Niskin bottle 24L",
"value": "niskin_24L",
"type": "str"
},
{
"label": "Pass Hull",
"value": "pass_hull",
"type": "str"
2021-12-03 02:51:25 +01:00
},
{
"label": "Single location (with net or bucket)",
"value": "single_location",
"type": "str"
},
{
"label": "Lab culture",
"value": "culture",
"type": "str"
},
{
"label": "Test",
"value": "test",
"type": "str"
2021-12-03 02:34:14 +01:00
}
],
"payload": "",
"topic": "sample_sampling_gear",
"topicType": "str",
2021-12-03 02:51:25 +01:00
"className": "",
2021-12-03 02:34:14 +01:00
"x": 630,
"y": 200,
2020-07-14 18:22:31 +02:00
"wires": [
2020-11-24 17:25:15 +01:00
[
2021-12-03 02:34:14 +01:00
"9f501f49.45645",
2021-12-03 02:51:25 +01:00
"46eb1bf8.3dc5f4",
"3ac7b631f5d8ef90"
2020-11-24 17:25:15 +01:00
]
2020-07-14 18:22:31 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "82c5fc77.59c97",
"type": "ui_text_input",
"z": "b771c342.49603",
"name": "sample_operator",
"label": "Name of the operator*",
2020-07-14 18:22:31 +02:00
"tooltip": "",
2021-12-03 02:34:14 +01:00
"group": "3e1ba03d.f01d8",
"order": 3,
"width": 0,
"height": 0,
"passthru": true,
"mode": "text",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "sample_operator",
"sendOnBlur": true,
"className": "",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"x": 650,
"y": 160,
2020-07-14 18:22:31 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"9f501f49.45645",
"52af9ac0.60eb24"
2020-07-14 18:22:31 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "9c882b37.fde668",
"type": "ui_text_input",
"z": "b771c342.49603",
"name": "sample_project",
"label": "Name of the project*",
2020-07-14 18:22:31 +02:00
"tooltip": "",
2021-12-03 02:34:14 +01:00
"group": "3e1ba03d.f01d8",
"order": 1,
"width": 0,
"height": 0,
"passthru": true,
"mode": "text",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "sample_project",
"sendOnBlur": true,
"className": "",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"x": 660,
"y": 40,
2020-07-14 18:22:31 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"9f501f49.45645"
2020-07-14 18:22:31 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "94eb4221.9b92c",
"type": "ui_text_input",
"z": "b771c342.49603",
"name": "sample_id",
"label": "Station ID*",
"tooltip": "",
"group": "3e1ba03d.f01d8",
"order": 4,
2020-07-14 18:22:31 +02:00
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"passthru": true,
"mode": "text",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "sample_id",
"sendOnBlur": true,
"className": "",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"x": 670,
"y": 120,
2020-07-14 18:22:31 +02:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"9f501f49.45645"
]
2020-07-14 18:22:31 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "9f501f49.45645",
2020-07-14 18:22:31 +02:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "b771c342.49603",
"name": "set global",
"func": "global.set(msg.topic,msg.payload);",
"outputs": 1,
2020-07-14 18:22:31 +02:00
"noerr": 0,
2020-11-24 17:25:15 +01:00
"initialize": "",
"finalize": "",
"libs": [],
"x": 1080,
2021-12-03 02:34:14 +01:00
"y": 200,
2020-07-14 18:22:31 +02:00
"wires": [
2020-11-24 17:25:15 +01:00
[]
2020-07-14 18:22:31 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "222c851d.5d0a3a",
"type": "ui_ui_control",
"z": "b771c342.49603",
"name": "",
"events": "change",
"x": 400,
"y": 880,
2020-11-24 17:25:15 +01:00
"wires": [
[]
]
2020-07-14 18:22:31 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "52f6b103.1efb6",
"type": "ui_toast",
"z": "b771c342.49603",
"position": "dialog",
"displayTime": "3",
"highlight": "",
"sendall": true,
"outputs": 1,
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
"name": "",
"x": 710,
"y": 980,
2020-11-24 17:25:15 +01:00
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "986d960a.c75908",
"type": "function",
"z": "b771c342.49603",
"name": "Check form",
"func": "var sample_project= global.get(\"sample_project\");\nvar sample_id= global.get(\"sample_id\");\nvar sample_operator= global.get(\"sample_operator\");\nvar sample_sampling_gear= global.get(\"sample_sampling_gear\");\nvar object_lat= global.get(\"object_lat\");\nvar object_lon= global.get(\"object_lon\");\nvar object_date= global.get(\"object_date\");\nvar object_time= global.get(\"object_time\");\nif (sample_project === undefined || sample_project === \"\") {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Sample project\"\n return [null, msg];\n}\n\nelse if (sample_id === undefined || sample_id === \"\") {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Sample ID\"\n return [null, msg];\n}\n\nelse if (sample_operator === undefined || sample_operator === \"\") {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Sample operator\"\n return [null, msg];\n}\n\nelse if (sample_sampling_gear === undefined || sample_sampling_gear === \"\") {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Sample sampling gear\"\n return [null, msg];\n}\n\nelse if (object_lat === undefined || object_lat === null) {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Latitude of sample\"\n return [null, msg];\n}\n\nelse if (object_lon === undefined || object_lon === null) {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Longitude of sample\"\n return [null, msg];\n}\n\nelse if (object_date === undefined || object_date === \"\") {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Date of sample\"\n return [null, msg];\n}\n\nelse if (object_time === undefined || object_time === \"\") {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Time of sample\"\n return [null, msg];\n}\nelse if (sample_sampling_gear.startsWith(\"net\")){\n var object_lat_end = global.get(\"object_lat_end\");\n var object_lon_end = global.get(\"object_lon_end\");\n var object_date_end = global.get(\"object_date_end\");\n var object_time_end = global.get(\"object_time_end\");\n var sample_gear_net_opening = global.get(\"sample_gear_net_opening\");\n\n if (object_lat_end === undefined || object_lat_end === null) {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Latitude of retrieval\"\n return [null, msg];\n }\n \n else if (object_lon_end === undefined || object_lon_end === null) {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Longitude of retrieval\"\n return [null, msg];\n }\n \n else if (object_date_end === undefined || object_date_end === \"\") {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Date of retrieval\"\n return [null, msg];\n }\n \n else if (object_time_end === undefined || object_time_end === \"\") {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Time of retrieval\"\n return [null, msg];\n }\n \n else if (sample_gear_net_opening === undefined || sample_gear_net_opening === \"\") {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Net opening dimension\"\n return [null, msg];\n }\n \n if (sample_sampling_gear == \"net_decknet\"){\n var sample_total_volume = global.get(\"sample_total_volume\");\n if (sample_total_volume === undefined || sample_total_volume === \"\") {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Decknet flowmeter readings\"\n return [null, msg];\n }\n }\n}\nmsg.topic = \"config_save\"\nmsg.payload={\"tab\":\"Optic Configuration\"};\n\nreturn [msg, null];",
"outputs": 2,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 470,
"y": 940,
2020-07-14 18:22:31 +02:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"726a7822.cd6298",
"e2b277c1.07283"
],
[
"52f6b103.1efb6"
]
],
"outputLabels": [
"message",
"error"
2020-07-14 18:22:31 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "18f44504.cac66b",
"type": "gpsd",
"z": "b771c342.49603",
"name": "",
"hostname": "localhost",
"port": "2947",
"tpv": true,
"sky": false,
"info": false,
"device": false,
"gst": false,
"att": false,
"x": 250,
"y": 1240,
"wires": [
[
"258b4562.9f778a",
"54e37580.fdc31c",
"cc21ca16.b92928",
"4a4de52c.cf2884",
"73c4a14a.9b93c8"
]
]
},
{
"id": "726a7822.cd6298",
2020-11-24 17:25:15 +01:00
"type": "ui_ui_control",
2021-12-03 02:34:14 +01:00
"z": "b771c342.49603",
2020-11-24 17:25:15 +01:00
"name": "",
"events": "change",
2021-12-03 02:34:14 +01:00
"x": 700,
"y": 900,
2020-11-24 17:25:15 +01:00
"wires": [
[]
]
},
{
"id": "16de754c.cc969b",
"type": "ui_button",
"z": "b771c342.49603",
"name": "",
"group": "5517c651.b2f668",
"order": 1,
2020-11-27 11:30:57 +01:00
"width": 5,
2020-11-24 17:25:15 +01:00
"height": 1,
"passthru": false,
"label": "Previous",
2020-07-14 18:22:31 +02:00
"tooltip": "",
2020-11-24 17:25:15 +01:00
"color": "#097479",
"bgcolor": "white",
"icon": "keyboard_return",
"payload": "{\"tab\":\"Home\"}",
"payloadType": "json",
"topic": "",
2020-12-03 23:13:08 +01:00
"x": 240,
"y": 880,
2020-07-14 18:22:31 +02:00
"wires": [
[
2020-11-24 17:25:15 +01:00
"222c851d.5d0a3a"
2020-07-14 18:22:31 +02:00
]
]
},
{
2020-11-24 17:25:15 +01:00
"id": "84f3d040.5f7ea",
"type": "ui_button",
"z": "b771c342.49603",
"name": "",
"group": "5517c651.b2f668",
"order": 2,
2020-11-27 11:30:57 +01:00
"width": 5,
2020-11-24 17:25:15 +01:00
"height": 1,
"passthru": false,
"label": "Continue",
2020-07-14 18:22:31 +02:00
"tooltip": "",
2020-11-24 17:25:15 +01:00
"color": "#097479",
"bgcolor": "white",
"icon": "keyboard_tab",
"payload": "{\"tab\":\"Home\"}",
"payloadType": "json",
"topic": "",
2020-12-03 23:13:08 +01:00
"x": 240,
"y": 940,
2020-07-14 18:22:31 +02:00
"wires": [
2020-11-24 17:25:15 +01:00
[
"986d960a.c75908"
]
2020-07-14 18:22:31 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d027a6bf.7049e8",
"type": "function",
"z": "b771c342.49603",
"name": "get sample_projet",
"func": "msg.payload = msg.payload.sample_project;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 310,
"y": 40,
2020-07-14 18:22:31 +02:00
"wires": [
2020-11-24 17:25:15 +01:00
[
2021-12-03 02:34:14 +01:00
"9c882b37.fde668"
2020-11-24 17:25:15 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "5a811caf.0f3144",
"type": "function",
"z": "b771c342.49603",
"name": "get sample_ship",
"func": "msg.payload = msg.payload.sample_ship;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
2021-12-03 02:51:25 +01:00
"x": 300,
2021-12-03 02:34:14 +01:00
"y": 80,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"4557d689.a4fa88"
2020-11-24 17:25:15 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "45911c98.2bd83c",
"type": "function",
"z": "b771c342.49603",
"name": "get sample_id",
"func": "msg.payload = msg.payload.sample_id;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 300,
"y": 120,
"wires": [
[
"94eb4221.9b92c"
]
]
},
{
"id": "1e09a4ab.72996b",
"type": "function",
"z": "b771c342.49603",
"name": "get sample_operator",
"func": "msg.payload = msg.payload.sample_operator;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 320,
"y": 160,
"wires": [
[
"82c5fc77.59c97"
]
]
},
{
"id": "a3272681.f271c8",
"type": "function",
"z": "b771c342.49603",
"name": "get sample_sampling_gear",
"func": "msg.topic = \"sample_sampling_gear\"\nif (msg.payload.sample_sampling_gear === undefined){\n msg.payload = \"net\";\n}\nelse\n{\n msg.payload = msg.payload.sample_sampling_gear;\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 340,
"y": 200,
"wires": [
[
"fcfc31ae.af3af"
]
]
},
{
"id": "a6907a38.f6611",
"type": "subflow:1c24ad9c.bebec2",
"z": "b771c342.49603",
2020-11-24 17:25:15 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"env": [],
"x": 70,
"y": 220,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"d027a6bf.7049e8",
"5a811caf.0f3144",
"45911c98.2bd83c",
"1e09a4ab.72996b",
"8dff1648.82e42",
"9f04c5ec.75f3d8",
"f408a273.4fb538",
"e73fd87d.d24e4",
"489c8e06.cc7d6",
"a3272681.f271c8"
2020-11-24 17:25:15 +01:00
]
2020-07-14 18:22:31 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "e2b277c1.07283",
"type": "subflow:1c24ad9c.bebec2",
"z": "b771c342.49603",
"name": "",
"env": [],
"x": 690,
"y": 940,
2020-07-14 18:22:31 +02:00
"wires": [
2020-11-25 16:58:32 +01:00
[]
2020-07-14 18:22:31 +02:00
]
},
2020-11-24 17:25:15 +01:00
{
2021-12-03 02:34:14 +01:00
"id": "7116e906.9f50f",
"type": "ui_text",
"z": "b771c342.49603",
"group": "fc5e4e6f.5b1c8",
"order": 1,
"width": 10,
"height": 1,
"name": "GPS Status Display",
"label": "GPS Status:",
"format": "{{msg.payload}}",
"layout": "row-center",
"x": 730,
"y": 1080,
2020-11-25 16:58:32 +01:00
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "9c7f7fc9.c8d3a",
"type": "ui_text_input",
"z": "b771c342.49603",
"name": "object_depth_max",
"label": "Max sampling depth (m)",
"tooltip": "in m",
"group": "3e1ba03d.f01d8",
"order": 9,
"width": 5,
"height": 1,
"passthru": true,
"mode": "number",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "object_depth_max",
"sendOnBlur": true,
"className": "",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"x": 650,
"y": 240,
2020-07-14 18:22:31 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"9f501f49.45645"
2020-07-14 18:22:31 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "317eeeb7.8d3042",
"type": "ui_text_input",
"z": "b771c342.49603",
"name": "object_depth_min",
"label": "Min sampling depth (m)",
"tooltip": "in m",
"group": "3e1ba03d.f01d8",
"order": 8,
"width": 5,
"height": 1,
"passthru": true,
"mode": "number",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "object_depth_min",
"sendOnBlur": true,
"className": "",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"x": 650,
"y": 280,
2020-11-24 17:25:15 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"9f501f49.45645"
]
2020-11-24 17:25:15 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "52af9ac0.60eb24",
"type": "link out",
"z": "b771c342.49603",
"name": "Sample Operator",
"links": [
"35eb925e.5f8016"
],
"x": 815,
"y": 160,
2020-11-25 16:58:32 +01:00
"wires": []
},
2020-07-14 18:22:31 +02:00
{
2021-12-03 02:34:14 +01:00
"id": "cbb123ab.fd3428",
"type": "ui_ui_control",
"z": "b771c342.49603",
"name": "show/hide net groups",
"events": "change",
"x": 1320,
"y": 300,
2020-11-24 17:25:15 +01:00
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "642ff403.1ed91c",
"type": "ui_text_input",
"z": "b771c342.49603",
"name": "sample_concentrated_sample_volume",
"label": "Concentrated sample volume (mL)",
"tooltip": "Volume extracted from the net codend (in mL)",
"group": "3e1ba03d.f01d8",
"order": 12,
"width": 10,
"height": 1,
"passthru": true,
"mode": "number",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "sample_concentrated_sample_volume",
"sendOnBlur": true,
"className": "",
2021-12-03 02:34:14 +01:00
"topicType": "str",
"x": 590,
"y": 440,
2020-07-14 18:22:31 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"9f501f49.45645"
2020-07-14 18:22:31 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "e967b844.46aa48",
"type": "ui_text_input",
"z": "b771c342.49603",
"name": "sample_gear_net_opening",
"label": "Net opening dimension (mm)",
"tooltip": "Size of the net mouth opening (in mm)",
"group": "cf5d9f0e.d57e7",
"order": 1,
"width": 10,
"height": 1,
2020-11-25 16:58:32 +01:00
"passthru": true,
2021-12-03 02:34:14 +01:00
"mode": "number",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "sample_gear_net_opening",
"sendOnBlur": true,
"className": "",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"x": 620,
"y": 320,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"9f501f49.45645"
2020-11-24 17:25:15 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "c0ce5626.b6c5",
"type": "ui_toast",
"z": "b771c342.49603",
"position": "dialog",
"displayTime": "3",
"highlight": "",
"sendall": true,
"outputs": 1,
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
"name": "",
"x": 1230,
"y": 620,
2020-07-14 18:22:31 +02:00
"wires": [
2021-12-03 02:34:14 +01:00
[]
2020-07-14 18:22:31 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "c33f1124.af6688",
"type": "ui_form",
"z": "b771c342.49603",
"name": "sample_location",
"label": "Sample Location",
"group": "cef1e703.bcf3c8",
"order": 1,
"width": 0,
"height": 0,
"options": [
{
"label": "Latitude (36.574439°N or 36°57.4439'N)",
"value": "object_lat",
"type": "text",
"required": true,
"rows": null
},
{
"label": "Longitude (110.42100°W or 110°4.2100'W)",
"value": "object_lon",
"type": "text",
"required": true,
"rows": null
},
{
"label": "Date (YYYY-MM-DD, UTC)",
"value": "object_date",
"type": "text",
"required": true,
"rows": null
},
{
"label": "Time (HH:MM(:SS), UTC 24h)",
"value": "object_time",
"type": "text",
"required": true,
"rows": null
}
],
"formValue": {
"object_lat": "",
"object_lon": "",
"object_date": "",
"object_time": ""
},
"payload": "",
"submit": "Validate",
"cancel": "Reset",
"topic": "sample_location",
"x": 660,
"y": 580,
2020-07-14 18:22:31 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"14658615.47c862"
2020-07-14 18:22:31 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "358908cd.416ab",
"type": "ui_form",
"z": "b771c342.49603",
"name": "net_throw_location",
"label": "Net Throw Location",
"group": "cf5d9f0e.d57e7",
2021-12-03 02:51:25 +01:00
"order": 3,
2021-12-03 02:34:14 +01:00
"width": 0,
"height": 0,
"options": [
2020-11-25 16:58:32 +01:00
{
2021-12-03 02:34:14 +01:00
"label": "Latitude (36.574439°N or 36°57.4439'N)",
"value": "object_lat",
"type": "text",
"required": true,
"rows": null
},
{
"label": "Longitude (110.42100°W or 110°4.2100'W)",
"value": "object_lon",
"type": "text",
"required": true,
"rows": null
},
{
"label": "Date (YYYY-MM-DD UTC)",
"value": "object_date",
"type": "text",
"required": true,
"rows": null
},
{
"label": "Time (HH:MM, UTC 24h)",
"value": "object_time",
"type": "text",
"required": true,
"rows": null
2020-11-25 16:58:32 +01:00
}
],
2021-12-03 02:34:14 +01:00
"formValue": {
"object_lat": "",
"object_lon": "",
"object_date": "",
"object_time": ""
},
"payload": "",
"submit": "Validate",
"cancel": "",
"topic": "net_throw_location",
"topicType": "str",
"splitLayout": false,
2021-12-03 02:51:25 +01:00
"className": "",
2021-12-03 02:34:14 +01:00
"x": 650,
"y": 620,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"14658615.47c862"
2020-11-25 16:58:32 +01:00
]
2020-11-24 17:25:15 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "56d40584.eff4e4",
"type": "ui_form",
"z": "b771c342.49603",
"name": "net_retrieval_location",
"label": "Net Retrieval Location",
"group": "cf5d9f0e.d57e7",
2021-12-03 02:51:25 +01:00
"order": 4,
2021-12-03 02:34:14 +01:00
"width": 0,
"height": 0,
"options": [
2020-11-25 16:58:32 +01:00
{
2021-12-03 02:34:14 +01:00
"label": "Latitude (36.574439°N or 36°57.4439'N)",
"value": "object_lat_end",
"type": "text",
"required": true,
"rows": null
},
{
"label": "Longitude (110.42100°W or 110°4.2100'W)",
"value": "object_lon_end",
"type": "text",
"required": true,
"rows": null
},
{
"label": "Date (YYYY-MM-DD UTC)",
"value": "object_date_end",
"type": "text",
"required": true,
"rows": null
},
{
"label": "Time (HH:MM, UTC 24h)",
"value": "object_time_end",
"type": "text",
"required": true,
"rows": null
2020-11-25 16:58:32 +01:00
}
],
2021-12-03 02:34:14 +01:00
"formValue": {
"object_lat_end": "",
"object_lon_end": "",
"object_date_end": "",
"object_time_end": ""
},
"payload": "",
"submit": "Validate",
"cancel": "",
"topic": "net_retrieval_location",
"topicType": "str",
"splitLayout": false,
"x": 640,
"y": 660,
2020-07-14 18:22:31 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"14658615.47c862"
2020-07-14 18:22:31 +02:00
]
]
},
2020-11-24 17:25:15 +01:00
{
2021-12-03 02:34:14 +01:00
"id": "14658615.47c862",
"type": "function",
"z": "b771c342.49603",
"name": "Validate Location / Timestamp",
2021-12-03 02:51:25 +01:00
"func": "// Code added here will be run once\n// whenever the node is started.\nfunction ConvertDDMMToDD(input) {\n // Input Format 36°57.4439'N, 110°4.2100'W\n // From https://stackoverflow.com/questions/1140189/converting-latitude-and-longitude-to-decimal-values\n if (!input.match(/\\d+°\\d+\\.*\\d*\\'[NSEW]/)){\n \treturn \"parsing error\"\n }\n var parts = input.split(/[^\\d.\\w]+/)\n if (parts.length != 3){\n \treturn \"parsing error\"\n }\n var dd = Number(parts[0]) + Number(parts[1])/60\n return dd.toFixed(6) + parts[2]\n}\n\nfunction ValidateCoordinates(input, lat){\n // Input Format 36.574439°N, 110.42100°W\n // Or 36°57.4439'N, 110°4.2100'W\n if (input.match(\"'\")){\n input = ConvertDDMMToDD(input)\n }\n \n var error = {}\n\n if (input.startsWith(\"parsing error\")){\n error.topic = \"Error with the \"\n error.payload = \"You need to respect the format example, 36.574439°N or 36°57.4439'N\"\n return [null, error]\n }\n \n var direction = input.match(/[NSEW]/)\n var position = input.match(/[\\+\\-\\d\\.]+/)\n \n if (direction === null){\n error.topic = \"Error with the \"\n error.payload = \"You need to explicitely enter N/S/E/W\"\n return [null, error]\n }\n \n // Test that position is only made of digits!\n if(/^[\\+\\-]/.test(position)){\n error.topic = \"Error with the \"\n error.payload = \"Use of +/- sign is inconsistent with N/S/E/W letter! Please only use N/S/E/W!\"\n return [null, error]\n }\n \n var dd = Number(position)\n if (lat){\n // Check latitude\n if (direction == \"S\" || direction == \"N\") {\n if (dd>90.0){\n error.topic = \"Error with the \"\n error.payload = \"Latitude is more than 90°\"\n return [null, error]\n }\n }\n if (direction == \"W\" || direction == \"E\") {\n error.topic = \"Error with the \"\n error.payload = \"This is not a Latitude!\"\n return [null, error]\n }\n }\n else{\n // Check longitude\n if (direction == \"W\" || direction == \"E\") {\n if (dd>180.0){\n error.topic = \"Error with the \"\n error.payload = \"Longitude is more than 180°\"\n return [null, error]\n }\n }\n if (direction == \"N\" || direction == \"S\") {\n error.topic = \"Error with the \"\n error.payload = \"This is not a Longitude!\"\n return [null, error]\n }\n }\n \n if (direction == \"S\" || direction == \"W\") {\n dd = dd * -1\n } // Don't do anything for N or E\n return [dd.toFixed(4), null]\n \n}\n\nfunction ValidateDate(input){\n // Input Format 2020-12-25\n var error = {};\n \n if (! /20\\d{2}-[0-1]\\d-[0-3]\\d/.test(input)){\n error.topic = \"Error with the date\";\n error.payload = \"The date should respect the ISO format YYYY-MM-DD\";\n return [null, error];\n }\n else {\n var date = input.match(/\\d+/g);\n if (!((2000 < date[0]) && (date[0] < 2100))){\n error.topic = \"Error with the date\"\n error.payload = \"The year should be between 2000 and 2100\"\n return [null, error]\n }\n else if (!((0 < date[1]) && (date[1] <= 12))){\n error.topic = \"Error with the date\"\n error.payload = \"The month should be between 01 and 12\"\n return [null, error]\n }\n else if (!((0 < date[2]) && (date[2] <= 31))){\n error.topic = \"Error with the date\"\n error.payload = \"The day should be between 01 and 31\"\n return [null, error]\n }\n }\n return [input.replace(/-/g, ''), null]\n}\n\nfunction ValidateTime(input){\n // Input Format 12:00\n var error = {}\n \n if (! /[0-2]?\\d:[0-5]\\d/.test(input)){\n error.topic = \"Erro
2021-12-03 02:34:14 +01:00
"outputs": 2,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 930,
"y": 620,
2020-07-14 18:22:31 +02:00
"wires": [
2020-11-24 17:25:15 +01:00
[
2021-12-03 02:34:14 +01:00
"c0ce5626.b6c5",
"9a18a4b4.178448"
],
[
"56d40584.eff4e4"
2020-11-24 17:25:15 +01:00
]
2021-12-03 02:34:14 +01:00
],
"inputLabels": [
"Location form data"
],
"outputLabels": [
"Message",
"Location validated"
2020-07-14 18:22:31 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "46eb1bf8.3dc5f4",
"type": "function",
"z": "b771c342.49603",
"name": "Net check",
2021-12-03 02:51:25 +01:00
"func": "var decknet = {}\nvar activation_msg = {}\n\nif (msg.payload.startsWith(\"net_decknet\")){\n decknet.enabled = true;\n}\nelse {\n decknet.enabled = false;\n}\n\nif (msg.payload.startsWith(\"net\")){\n activation_msg.payload = {\"group\":{\"show\":[\"Sample_Net_Metadata\"],\"hide\":[\"Sample_Sample_Location\",\"Sample_Culture_Date_and_Time\"]}};\n}\nelse if (msg.payload != \"culture\" && msg.payload != \"test\"){\n activation_msg.payload = {\"group\":{\"show\":[\"Sample_Sample_Location\"], \"hide\":[\"Sample_Net_Metadata\",\"Sample_Culture_Date_and_Time\"]}};\n}\n\n\nreturn [decknet, activation_msg];",
2021-12-03 02:34:14 +01:00
"outputs": 2,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1080,
"y": 240,
2020-07-14 18:22:31 +02:00
"wires": [
2020-11-24 17:25:15 +01:00
[
2021-12-03 02:34:14 +01:00
"516375fd.2ed61c",
"470e382a.25691",
"fbe32ac8.ff6a38"
],
[
"cbb123ab.fd3428"
2020-11-24 17:25:15 +01:00
]
2021-12-03 02:34:14 +01:00
],
"outputLabels": [
"decknet activation",
"group display control"
2020-07-14 18:22:31 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "516375fd.2ed61c",
"type": "ui_text_input",
"z": "b771c342.49603",
"name": "sample_total_flowmeter_start",
"label": "Decknet flowmeter start",
"tooltip": "in L",
"group": "cf5d9f0e.d57e7",
2021-12-03 02:51:25 +01:00
"order": 6,
"width": 5,
2020-11-25 16:58:32 +01:00
"height": 1,
"passthru": false,
2021-12-03 02:34:14 +01:00
"mode": "text",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "sample_total_flowmeter_start",
"sendOnBlur": true,
"className": "",
2021-12-03 02:34:14 +01:00
"topicType": "str",
2021-12-03 02:51:25 +01:00
"x": 1340,
2021-12-03 02:34:14 +01:00
"y": 220,
2020-07-14 18:22:31 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"e9bc112c.eb75f8"
2020-07-14 18:22:31 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "470e382a.25691",
"type": "ui_text_input",
"z": "b771c342.49603",
"name": "sample_total_flowmeter_end",
"label": "Decknet flowmeter end",
"tooltip": "in L",
"group": "cf5d9f0e.d57e7",
2021-12-03 02:51:25 +01:00
"order": 7,
2021-12-03 02:34:14 +01:00
"width": 5,
2020-11-25 16:58:32 +01:00
"height": 1,
2021-12-03 02:34:14 +01:00
"passthru": false,
"mode": "text",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "sample_total_flowmeter_end",
"sendOnBlur": true,
"className": "",
2021-12-03 02:34:14 +01:00
"topicType": "str",
"x": 1340,
"y": 260,
2020-07-14 18:22:31 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"e9bc112c.eb75f8"
2020-07-14 18:22:31 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "fbe32ac8.ff6a38",
"type": "ui_template",
"z": "b771c342.49603",
"group": "cf5d9f0e.d57e7",
"name": "Decknet flowmeter read",
2021-12-03 02:51:25 +01:00
"order": 5,
2021-12-03 02:34:14 +01:00
"width": 10,
"height": 1,
"format": "<div style=\"line-height: 100%;\">\n <p class=\"formlabel\" style=\"font-size: larger;\">Decknet flowmeter readings</p>\n <p style=\"font-size: smaller;\">Those values are used to calculate <font style=\"font-family: mono;\">sample_total_volume</font>. Values are in L.</p>\n</div>\n",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"x": 1330,
"y": 180,
"wires": [
[]
]
2020-11-25 16:58:32 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "f408a273.4fb538",
"type": "function",
"z": "b771c342.49603",
"name": "get sample_gear_net_opening",
"func": "if (msg.payload.sample_gear_net_opening === null){\n msg.payload = 0;\n global.set(\"sample_gear_net_opening\",msg.payload);\n}\nelse\n{\n msg.payload = msg.payload.sample_gear_net_opening;\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 350,
"y": 320,
"wires": [
[
2021-12-03 02:34:14 +01:00
"e967b844.46aa48"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "8dff1648.82e42",
"type": "function",
"z": "b771c342.49603",
"name": "get object_depth_max",
"func": "if (msg.payload.object_depth_max === null){\n msg.payload = 0;\n global.set(\"object_depth_max\",msg.payload);\n}\nelse\n{\n msg.payload = msg.payload.object_depth_max;\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 320,
"y": 240,
2020-07-14 18:22:31 +02:00
"wires": [
2020-11-24 17:25:15 +01:00
[
2021-12-03 02:34:14 +01:00
"9c7f7fc9.c8d3a"
2020-11-24 17:25:15 +01:00
]
2020-07-14 18:22:31 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "9f04c5ec.75f3d8",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "b771c342.49603",
"name": "get object_depth_min",
"func": "if (msg.payload.object_depth_min === null){\n msg.payload = 0;\n global.set(\"object_depth_min\",msg.payload);\n}\nelse\n{\n msg.payload = msg.payload.object_depth_min;\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
2021-12-03 02:34:14 +01:00
"x": 320,
"y": 280,
2020-07-14 18:22:31 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"317eeeb7.8d3042"
2020-11-24 17:25:15 +01:00
]
]
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "9a18a4b4.178448",
"type": "function",
"z": "b771c342.49603",
"name": "calculate sample_total_volume",
"func": "\n/*object_lat = 33.95 \nobject_lon = 118.4 \nobject_lat_end = 40.6333 \nobject_lon_end = 73.78333\nsample_gear_net_opening = 40*/\n\n// Copyright 1997 Ed Williams. All rights reserved\n// Adapted by Romain Bazile - Ocean Trotter - 01-2021\n\ndecpl=4 // Dec places of minutes output\n\nellipse = {\"name\":\"WSG84\", \"a\":6378.137/1.852, \"invf\":298.257223563}\n\nfunction ComputeDistance(lat1, lon1, lat2, lon2){\n var d,crs12,crs21\n var argacos\n var a,invf\n \n // lat and lon in radian\n lat1=(Math.PI/180)*lat1\n lat2=(Math.PI/180)*lat2\n lon1=(Math.PI/180)*lon1\n lon2=(Math.PI/180)*lon2\n \n //alert(\"lat1=\" + lat1 + \"lon1=\" + lon1 +\"\\nlat2=\" +lat2+ \"lon2=\"+lon2)\n \n /* get distance conversion factor */\n dc=1.852 //km\n //alert(\"dc=\" +dc)\n \n //showProps(ellipse,\"ellipse\")\n \n // elliptic code\n d=crsdist_ell(lat1,-lon1,lat2,-lon2,ellipse) // ellipse uses East negative\n d=d*dc // go to physical units\n \n //alert(\"d=\"+d+\" crs12=\"+crs12+\" crs21=\"+crs21)\n return d\n}\n\nfunction crsdist(lat1,lon1,lat2,lon2){ // radian args\n /* compute course and distance (spherical) */\n if ((lat1+lat2===0) && (Math.abs(lon1-lon2)==Math.PI) && \n (Math.abs(lat1) != (Math.PI/180)*90)){\t\n \talert(\"Course between antipodal points is undefined\")\n }\n \n d = Math.acos(Math.sin(lat1)*Math.sin(lat2)+Math.cos(lat1)*Math.cos(lat2)*Math.cos(lon1-lon2))\n return d\n}\n\nfunction crsdist_ell(glat1,glon1,glat2,glon2,ellipse){\n // glat1 initial geodetic latitude in radians N positive \n // glon1 initial geodetic longitude in radians E positive \n // glat2 final geodetic latitude in radians N positive \n // glon2 final geodetic longitude in radians E positive \n a=ellipse.a\n f=1/ellipse.invf\n //alert(\"a=\"+a+\" f=\"+f)\n var r, tu1, tu2, cu1, su1, cu2, s1, b1, f1\n var x, sx, cx, sy, cy,y, sa, c2a, cz, e, c, d\n var EPS= 0.00000000005\n var faz, baz, s\n var iter=1\n var MAXITER=100\n if ((glat1+glat2===0) && (Math.abs(glon1-glon2)==Math.PI)){\n alert(\"Course and distance between antipodal points is undefined\")\n glat1=glat1+0.00001 // allow algorithm to complete\n }\n if (glat1==glat2 && (glon1==glon2 || Math.abs(Math.abs(glon1-glon2)-2*Math.PI) < EPS)){\n alert(\"Points 1 and 2 are identical- course undefined\")\n out=new MakeArray(0)\n out.d=0\n out.crs12=0\n out.crs21=Math.PI\n return out\n }\n r = 1 - f\n tu1 = r * Math.tan (glat1)\n tu2 = r * Math.tan (glat2)\n cu1 = 1 / Math.sqrt (1 + tu1 * tu1)\n su1 = cu1 * tu1\n cu2 = 1 / Math.sqrt (1 + tu2 * tu2)\n s1 = cu1 * cu2\n b1 = s1 * tu2\n f1 = b1 * tu1\n x = glon2 - glon1\n d = x + 1 // force one pass\n while ((Math.abs(d - x) > EPS) && (iter < MAXITER))\n {\n iter=iter+1\n sx = Math.sin (x)\n // alert(\"sx=\"+sx)\n cx = Math.cos (x)\n tu1 = cu2 * sx\n tu2 = b1 - su1 * cu2 * cx\n sy = Math.sqrt(tu1 * tu1 + tu2 * tu2)\n cy = s1 * cx + f1\n y = atan2 (sy, cy)\n sa = s1 * sx / sy\n c2a = 1 - sa * sa\n cz = f1 + f1\n if (c2a > 0)\n cz = cy - cz / c2a\n e = cz * cz * 2 - 1\n c = ((-3 * c2a + 4) * f + 4) * c2a * f / 16\n d = x\n x = ((e * cy * c + cz) * sy * c + y) * sa\n x = (1 - c) * x * f + glon2 - glon1\n }\n x = Math.sqrt ((1 / (r * r) - 1) * c2a + 1)\n x +=1\n x = (x - 2) / x\n c = 1 - x\n c = (x * x / 4 + 1) / c\n d = (0.375 * x * x - 1) * x\n x = e * cy\n d = ((((sy*sy*4-3)*(1-e-e)*cz*d/6-x)*d/4+cz)*sy*d+y)*c*a*r\n if (Math.abs(iter-MAXITER)<EPS){\n alert(\"Algorithm did not converge\")\n }\n return d\n}\n\n\n//***************Utility***************\nfunction atan2(y,x){\n var out\n if (x <0) { out= Math.atan(y/x)+Math.PI}\n if ((x >0) && (y>=0)){ out= Math.atan(y/x)}\n if (
2020-11-25 16:58:32 +01:00
"outputs": 1,
2021-12-03 02:34:14 +01:00
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 930,
"y": 760,
2020-11-25 16:58:32 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"4f6afc5a.81e454"
]
2020-11-25 16:58:32 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4fb4e0ad.c417c",
"type": "ui_text",
"z": "b771c342.49603",
"group": "fc5e4e6f.5b1c8",
"order": 6,
"width": 5,
"height": 1,
"name": "Latitude",
"label": "Latitude",
"format": "{{msg.payload.lat.deg}}&deg{{msg.payload.lat.min}}'{{msg.payload.lat.sec}}{{msg.payload.lat.dir}}",
"layout": "col-center",
"x": 700,
"y": 1120,
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "8d2b5026.13e6e8",
"type": "ui_text",
"z": "b771c342.49603",
"group": "fc5e4e6f.5b1c8",
"order": 7,
2020-11-25 16:58:32 +01:00
"width": 5,
2020-11-24 17:25:15 +01:00
"height": 1,
2021-12-03 02:34:14 +01:00
"name": "Longitude",
"label": "Longitude",
"format": "{{msg.payload.lon.deg}}&deg{{msg.payload.lon.min}}'{{msg.payload.lon.sec}}{{msg.payload.lon.dir}}",
"layout": "col-center",
"x": 700,
"y": 1160,
"wires": []
},
{
"id": "258b4562.9f778a",
"type": "function",
"z": "b771c342.49603",
"name": "Convert DD to DMS",
"func": "function ConvertDDToDMS(D, lng){\n // from https://stackoverflow.com/a/5786281/2108279\n return {\n dir : D<0?lng?'W':'S':lng?'E':'N',\n deg : 0|(D<0?D=-D:D),\n min : 0|D%1*60,\n sec :(0|D*60%1*6000)/100\n };\n}\n\nmsg.payload = {\n \"lat\":ConvertDDToDMS(msg.payload.lat, false),\n \"lon\":ConvertDDToDMS(msg.payload.lon, true)\n};\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:51:25 +01:00
"x": 490,
2021-12-03 02:34:14 +01:00
"y": 1140,
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"4fb4e0ad.c417c",
"8d2b5026.13e6e8"
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "7c4ce5f3.62dd5c",
"type": "ui_text",
"z": "b771c342.49603",
"group": "fc5e4e6f.5b1c8",
"order": 4,
"width": 2,
"height": 1,
"name": "Speed",
"label": "Speed",
"format": "{{msg.payload}} kts",
"layout": "col-center",
"x": 690,
"y": 1200,
2020-11-25 16:58:32 +01:00
"wires": []
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "54e37580.fdc31c",
"type": "ui_text",
"z": "b771c342.49603",
"group": "fc5e4e6f.5b1c8",
"order": 5,
"width": 2,
2020-11-25 16:58:32 +01:00
"height": 1,
2021-12-03 02:34:14 +01:00
"name": "Direction",
"label": "Direction",
"format": "{{msg.payload.track}} °",
"layout": "col-center",
"x": 700,
"y": 1240,
"wires": []
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "cc21ca16.b92928",
2020-11-25 16:58:32 +01:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "b771c342.49603",
"name": "GPS Mode",
"func": "switch (msg.payload.mode){\n case 1:msg.payload = \"No Fix\"; break\n case 2:msg.payload = \"2D Fix\"; break\n case 3:msg.payload = \"3D Fix\"; break\n default: msg.payload = \"No info\"\n}\nreturn msg;",
2020-11-25 16:58:32 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 470,
"y": 1080,
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"wires": [
2020-11-25 16:58:32 +01:00
[
2021-12-03 02:34:14 +01:00
"7116e906.9f50f"
2020-11-25 16:58:32 +01:00
]
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
]
2020-11-24 17:25:15 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "4a4de52c.cf2884",
"type": "function",
"z": "b771c342.49603",
"name": "Speed conversion",
"func": "msg.payload = (0|msg.payload.speed) * 1.9438\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 490,
"y": 1200,
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"7c4ce5f3.62dd5c"
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "35ad311f.344c76",
"type": "ui_text",
"z": "b771c342.49603",
"group": "fc5e4e6f.5b1c8",
"order": 2,
2020-11-25 16:58:32 +01:00
"width": 5,
2020-10-06 17:22:25 +02:00
"height": 1,
2021-12-03 02:34:14 +01:00
"name": "Time",
"label": "Time",
"format": "{{msg.payload.time}}",
"layout": "col-center",
"x": 690,
"y": 1280,
"wires": []
2020-11-25 16:58:32 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "e73fd87d.d24e4",
"type": "function",
"z": "b771c342.49603",
"name": "get acq_minimum_mesh",
"func": "msg.payload = msg.payload.acq_minimum_mesh;\nreturn msg;",
2020-11-25 16:58:32 +01:00
"outputs": 1,
2021-12-03 02:34:14 +01:00
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 330,
"y": 360,
2020-11-25 16:58:32 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"1aad56b31b5647ce"
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "489c8e06.cc7d6",
2020-11-24 17:25:15 +01:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "b771c342.49603",
"name": "get acq_maximum_mesh",
"func": "msg.payload = msg.payload.acq_maximum_mesh;\nreturn msg;",
2020-11-24 17:25:15 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"libs": [],
"x": 330,
"y": 400,
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"10eb31a2d7a6590c"
]
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
]
},
2020-11-25 16:58:32 +01:00
{
2021-12-03 02:34:14 +01:00
"id": "73c4a14a.9b93c8",
2020-11-25 16:58:32 +01:00
"type": "function",
"z": "b771c342.49603",
2021-12-03 02:34:14 +01:00
"name": "Convert time",
"func": "if (msg.payload.time){\n msg.payload.time = msg.payload.time.replace('T', ' ');\n return msg;\n}",
2020-11-25 16:58:32 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
2021-12-03 02:34:14 +01:00
"x": 470,
"y": 1280,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"35ad311f.344c76"
2020-11-24 17:25:15 +01:00
]
]
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "58de1340.3cc354",
"type": "ui_text_input",
2020-11-25 16:58:32 +01:00
"z": "b771c342.49603",
2021-12-03 02:34:14 +01:00
"name": "sample_dilution_factor",
"label": "Dilution Factor",
"tooltip": "0.5 if diluted by two; 2 if concentrated by a factor 2",
"group": "3e1ba03d.f01d8",
"order": 13,
"width": 10,
"height": 1,
"passthru": true,
"mode": "number",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "sample_dilution_factor",
"sendOnBlur": true,
"className": "",
2021-12-03 02:34:14 +01:00
"topicType": "str",
"x": 640,
"y": 480,
2020-11-25 16:58:32 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"9f501f49.45645"
2020-11-25 16:58:32 +01:00
]
]
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "4f6afc5a.81e454",
"type": "ui_text_input",
2020-11-25 16:58:32 +01:00
"z": "b771c342.49603",
2021-12-03 02:34:14 +01:00
"name": "sample_total_volume",
"label": "Filtered volume (in L)",
"tooltip": "Calculated or hand filled",
"group": "3e1ba03d.f01d8",
"order": 11,
"width": 0,
"height": 0,
"passthru": true,
"mode": "text",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "sample_total_volume",
"sendOnBlur": true,
"className": "",
2021-12-03 02:34:14 +01:00
"topicType": "str",
"x": 1220,
"y": 760,
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"wires": [
2020-11-25 16:58:32 +01:00
[
2021-12-03 02:34:14 +01:00
"e1f2f6eb.fe1dd"
2020-11-25 16:58:32 +01:00
]
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "e1f2f6eb.fe1dd",
2020-11-25 16:58:32 +01:00
"type": "function",
"z": "b771c342.49603",
2021-12-03 02:34:14 +01:00
"name": "set global",
"func": "global.set(msg.topic,msg.payload);",
2020-11-25 16:58:32 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 1420,
"y": 760,
2020-11-25 16:58:32 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[]
2020-11-25 16:58:32 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "e9bc112c.eb75f8",
2020-11-25 16:58:32 +01:00
"type": "function",
"z": "b771c342.49603",
2021-12-03 02:34:14 +01:00
"name": "Calculate volume",
"func": "if (msg.topic == \"sample_total_flowmeter_start\"){\n context.set(\"sample_total_flowmeter_start\", msg.payload);\n}\nif (msg.topic == \"sample_total_flowmeter_end\"){\n context.set(\"sample_total_flowmeter_end\", msg.payload);\n}\n\nif (context.keys().length == 2){\n sample_total_volume = context.get(\"sample_total_flowmeter_end\") - context.get(\"sample_total_flowmeter_start\");\n msg.topic=\"sample_total_volume\"\n msg.payload=sample_total_volume\n return msg\n}\n",
2020-11-25 16:58:32 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 1610,
"y": 240,
2020-11-25 16:58:32 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"4f6afc5a.81e454"
2020-11-25 16:58:32 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "cdd4181922eecf11",
"type": "ui_text_input",
"z": "b771c342.49603",
"name": "sample_speed_through_water",
"label": "Speed Through Water (kts)",
"tooltip": "in knots",
"group": "cf5d9f0e.d57e7",
"order": 2,
"width": 10,
"height": 1,
"passthru": true,
"mode": "number",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "sample_speed_through_water",
"topicType": "str",
"x": 610,
"y": 520,
2020-11-25 16:58:32 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"9f501f49.45645"
2020-11-25 16:58:32 +01:00
]
2021-12-03 02:34:14 +01:00
]
2020-11-25 16:58:32 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "1aad56b31b5647ce",
"type": "ui_text_input",
"z": "b771c342.49603",
"name": "acq_minimum_mesh",
"label": "Minimal fraction size (μm)",
"tooltip": "Net mesh pore size or minimal filtration mesh pore size",
"group": "3e1ba03d.f01d8",
"order": 7,
"width": 5,
"height": 1,
"passthru": true,
"mode": "number",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "acq_minimum_mesh",
"topicType": "str",
"x": 640,
"y": 360,
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"9f501f49.45645"
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "10eb31a2d7a6590c",
"type": "ui_text_input",
"z": "b771c342.49603",
"name": "acq_maximum_mesh",
"label": "Maximal fraction size (μm)",
"tooltip": "Maximal filtration mesh pore size",
"group": "3e1ba03d.f01d8",
"order": 8,
"width": 5,
"height": 1,
"passthru": true,
"mode": "number",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "acq_maximum_mesh",
"topicType": "str",
"x": 640,
"y": 400,
2020-11-24 17:25:15 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"9f501f49.45645"
]
2020-11-24 17:25:15 +01:00
]
},
2021-12-03 02:51:25 +01:00
{
"id": "3ac7b631f5d8ef90",
"type": "function",
"z": "b771c342.49603",
"name": "Culture check",
"func": "var activation_msg = {}\nvar date_msg = {}\nvar timestamp=new Date().toISOString();\n\nif (msg.payload === \"culture\"){\n activation_msg.payload = {\"group\":{\"show\":[\"Sample_Culture_Date_and_Time\"],\"hide\":[\"Sample_Sample_Location\", \"Sample_Net_Metadata\"]}};\n global.set(\"object_lat\", \"-90.0000\")\n global.set(\"object_lon\", \"0.0000\")\n date_msg.payload = {\"object_date\": timestamp.split('T')[0],\n \"object_time\": timestamp.split('T')[1].split('.')[0]}\n return [activation_msg, date_msg];\n}\nelse if (msg.payload === \"test\"){\n activation_msg.payload = {\"group\":{\"hide\":[\"Sample_Culture_Date_and_Time\", \"Sample_Sample_Location\", \"Sample_Net_Metadata\"]}};\n global.set(\"object_lat\", \"-90.0000\")\n global.set(\"object_lon\", \"0.0000\")\n global.set(\"object_date\", timestamp.split('T')[0])\n global.set(\"object_time\", timestamp.split('T')[1].split('.')[0])\n return [activation_msg, null];\n}\nelse{\n return [null, null]\n}\n\n",
"outputs": 2,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1100,
"y": 320,
"wires": [
[
"cbb123ab.fd3428"
],
[
"05c6aff2afbd69cf"
]
],
"outputLabels": [
"decknet activation",
""
]
},
{
"id": "05c6aff2afbd69cf",
"type": "ui_form",
"z": "b771c342.49603",
"name": "culture_timestamp",
"label": "Culture timestamp",
"group": "7bc0a4c416e4545c",
"order": 1,
"width": 0,
"height": 0,
"options": [
{
"label": "Date (YYYY-MM-DD, UTC)",
"value": "object_date",
"type": "text",
"required": true,
"rows": null
},
{
"label": "Time (HH:MM(:SS), UTC 24h)",
"value": "object_time",
"type": "text",
"required": true,
"rows": null
}
],
"formValue": {
"object_date": "",
"object_time": ""
},
"payload": "",
"submit": "Validate",
"cancel": "Reset",
"topic": "culture_timestamp",
"topicType": "str",
"splitLayout": false,
"className": "",
"x": 650,
"y": 700,
"wires": [
[
"14658615.47c862"
]
]
},
2020-11-24 17:25:15 +01:00
{
2021-12-03 02:34:14 +01:00
"id": "6a84252a.d52a0c",
"type": "ui_template",
"z": "bccd1f23.87219",
"group": "833bc5bb.217ba8",
"name": "Stream Pi Camera",
"order": 1,
"width": 18,
"height": 14,
"format": "<span ng-bind-html=\"msg.payload\"></span>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": false,
"templateScope": "local",
"x": 610,
"y": 40,
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"wires": [
2021-12-03 02:34:14 +01:00
[]
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "dc48dc42.98d18",
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "bccd1f23.87219",
"name": "set global",
"func": "global.set(msg.topic,msg.payload);",
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 880,
"y": 440,
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"wires": [
2020-11-25 16:58:32 +01:00
[]
]
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "811cd88c.daf528",
"type": "ui_button",
"z": "bccd1f23.87219",
"name": "down",
"group": "fbd92986.1028c8",
"order": 6,
"width": 4,
"height": 1,
"passthru": true,
"label": "",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "fa-angle-double-down fa-3x",
"payload": "DOWN",
"payloadType": "str",
2021-12-03 02:34:14 +01:00
"topic": "actuator/focus",
"x": 550,
"y": 800,
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"65ad39d.b6d4d48"
2020-11-25 16:58:32 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "edda4df4.76de2",
"type": "ui_button",
"z": "bccd1f23.87219",
"name": "up",
"group": "fbd92986.1028c8",
"order": 3,
"width": 4,
"height": 1,
"passthru": false,
"label": "",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "fa-angle-double-up fa-3x",
"payload": "UP",
"payloadType": "str",
"topic": "actuator/focus",
"x": 550,
"y": 760,
2020-11-25 16:58:32 +01:00
"wires": [
2020-11-24 17:25:15 +01:00
[
2021-12-03 02:34:14 +01:00
"65ad39d.b6d4d48"
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "68962547.34a67c",
"type": "ui_text_input",
"z": "bccd1f23.87219",
"name": "pump_manual_volume",
"label": "Volume to pass (ml)",
"tooltip": "Tiny values are accepted down to 0.001mL",
"group": "707d9797.c8e798",
"order": 3,
"width": 2,
"height": 1,
"passthru": true,
"mode": "text",
"delay": "300",
2021-12-03 02:34:14 +01:00
"topic": "pump_manual_volume",
"sendOnBlur": true,
"className": "",
"topicType": "str",
2021-12-03 02:51:25 +01:00
"x": 600,
2021-12-03 02:34:14 +01:00
"y": 420,
2020-11-24 17:25:15 +01:00
"wires": [
2020-11-25 16:58:32 +01:00
[
2021-12-03 02:34:14 +01:00
"dc48dc42.98d18"
2020-11-25 16:58:32 +01:00
]
2020-11-24 17:25:15 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6c792043.b6ff9",
"type": "ui_ui_control",
"z": "bccd1f23.87219",
Extraction and refactor of the python code from node-red flow The rationale for this rewrite is to improve the readability, the modularity, the reliability and the future-proofing of the main python script. All in all, this is now 124 commits that are going to be squashed and merged together, spanning more than two weeks of development and testing. Please test away this release and break things. An upgrading guide will be published in the coming days, along with a new image for people to use if they don't want to upgrade on their own. Read along if you want to know all the goodies! As a starter, the python script was extracted from the main flow, and now lives in its own files at `PlantonScope/scripts/*`. We set up the auto formatting of the code by using [Black](https://github.com/psf/black). This make the code clearer and uniform. We are using the default settings, so if you just install Black and set your editor to format on save using it, you should be good to go. The code is separated in four main processes, each with a specific set of responsibilities: - The main process controls all the others, starts everything up and cleans up on shutdown - The stepper process manages the stepper movements. It's now possible to have simultaneous movements of both motors (this closes #38 ). - The imager process controls the camera and the streaming server via a state machine. - The segmenter process manages the segmentation and its outputs. The segmentation happens recursively in all folders in `/home/pi/PlanktonScope/img/`. Each folder has its own output archive, and bug #26 is now closed. Those processes communicates together using MQTT and json messages. Each message is adressed to one topic. The high level topic controls which process receives the message. The details of each topic is at the end of this commit message. Every imaging sessions has now its own folder, under the `img` root. Metadata are saved individually for every session, in a JSON file in the same directory as the pictures. The configuration is not parsed from `config.json` anymore and passed directly through MQTT messages to the concerned process. A new configuration file has been created: `hardware.json`. This file contains information related to your specific hardware configuration. You can choose to reverse the connection of the motors for example, but you can also define specific speed limits and steps number for your pump and focus stage. This will make it easier for people who wants to experiment with different kind of hardware. It's not necessary to have this file though. If it doesn't exists, the default configuration will be applied. The code is architectured around 6 modules and about 10 classes. I encourage you to have a look at the files, they're pretty straightforward to understand. There is a lot of work left around the node-red code refactoring, dashboard ui improvements, better and clearer LED messages, OLED screen integration and finer control of the segmentation process, but this is quite good for now. Here is the topic lists for MQTT and the corresponding messages. - actuator : This topic adresses the stepper control thread No publication under this topic should happen from the python process - actuator/pump : Control of the pump The message is a json object {"action":"move", "direction":"FORWARD", "volume":10, "flowrate":1} to move 10mL forward at 1mL/min action can be "move" or "stop" Receive only - actuator/focus : Control of the focus stage The message is a json object, speed is optional {"action":"move", "direction":"UP", "distance":0.26, "speed":1} to move up 10mm action can be "move" or "stop" Receive only - imager/image : This topic adresses the imaging thread Is a json object with {"action":"image","sleep":5,"volume":1,"nb_frame":200} sleep in seconds, volume in mL Can also receive a config update message: {"action":"config","config":[...]} Can also receive a camera settings message: {"action":"settings","iso":100,"shutter_speed":40} Receive only - segmenter/segment : This topic adresses the segmenter process Is a json object with {"action":"segment"} Receive only - status : This topics sends feedback to Node-Red No publication or receive at this level - status/pump : State of the pump Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/focus : State of the focus stage Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Done, Interrupted Publish only - status/imager : State of the imager Is a json object with {"status":"Start", "time_left":25} Status is one of Started, Ready, Completed or 12_11_15_0.1.jpg has been imaged. Publish only - status/segmenter : Status of the segmentation - status/segmenter/name - status/segmenter/object_id - status/segmenter/metric Here is the original commit history: * Extract python main.py from flow * Fix bug in server addresses These addresses should be the loopback device instead of the network address of the device. Using the loopback address will not necessitate to update the script when the network address changes. * clean up picamera import * changes to main python and flow: update MQTT requests address to localhost (bugfix) update streaming output address to nothing update main flow to remove python script references and location * Automatically initialise imaging led on startup to off state. * Add the ability to invert outputs of the motor We added a key to config.json "hardware_config" with a subkey "stepper_reverse". If this key is present in the config file and set to 1, the output of the motors are inversed (stepper2 becomes the pump motor and stepper1 the focus motor) * move all non main script to a subfolder * add __init__.py to package * light module rewrite * json cleanup and absolute path for config file * light.py forgot to import subprocess #oups * Add command to turn the leds off * Auto formatting of main.py I've used Black with default settings, see https://github.com/psf/black * First commit of stepper.py Pump parameters still needs to be checked and tuned. * addition of hardware details in config.json * Introduce hardware.json to replace the `hardware_config` of config.json * stepper.py: calibration, typos * creates the MQTT_Client class * pump_max_speed is now in ml/min to help readability * forgot to add self to the class def * addition of threading capabilities to stepper.py (UNTESTED) * mqtt: fix topic bug * remove counter * mqtt add doc about topics * stepper.py creates an "actuator/*/state" topic * stepper.py: rename mqtt_client to pump_client * mqtt.py: add details about topics * stepper.py: rename pump_client to actuator_client * topic was not split properly and a part was lost * switch to f-strings for mqtt.py * cosmetic update * stepper.py: folder name will be planktoscope change calls * hardware.json became more straightforward * stepper.py syntax bugs * stepper.py addition of a received stop command * stepper.py: update to max travel distance * stepper.py: several typos here * rename folder * main.py: reword to reflect folder rename * main.py: remove logic that has been moved to stepper.py and mqtt.py * main.py: update to add mqtt imaging client * mqtt.py: make command and args local to class and output more verbose * make stepper.py a class * main.py: instantiate stepper class and call it * main.py: name mqtt client * update to main.json to reflect main.py changes * fix bugs around pump control * update flows to latest version from Thibault * distance can be a small value, and definitevely should be a float. * unify mqtt topics * unify mqtt output in the main flow * first logger implementation, uses loguru * mqtt: add reason to on_connect * mqtt: add on_disconnect handler * stepper: add more logger calls for debug mainly * main: add levels for logger * imager.py: first move of the imager logic * imager: time import cleanup * imager: morphocut import cleanup * imager: skimage import cleanup * imager: finishing import cleanup * imager: Class creation - WIP Also provides a fix for #26 (see line 190). * imager: threading is needed for Condition() * streamer: get the streamer server its own file * imager: creates start_camera and get the server creation out * imager: subclass multiprocessing.Process * imager: get Pipeline creation its own function * imager: cleanup of self calls * main: code removal and corresponding calls to newly created classes * imager: various formatting changes * main: management of signal shutdown * add requirements.txt * mqtt: messages are now json objects Also, addition of a flag on receiving a new message * mqtt: make message private and add logic to synchronise * stepper: creates the stepper class * stepper: use the new class * stepper: uses the new logic * stepper: add the shutdown event * stepper: add shutdown method * main: add shutdown event * imager: graceful shutdown * stepper: nicer way of checking the Eevnt * self is a required first argument for a method in a class Especially if you use said class private members! * python: various typos and small errors in import * stepper: create mqtt client during init * stepper: instanciate the mqtt client inside run Otherwise it's not accessible from inside the loop. It's a PITA, more information at https://stackoverflow.com/questions/17172878/using-pythons-multiprocessing-process-class * stepper: little bugs and typos all around * mqtt: add shutdown method * mqtt: add connect in init * stepper: fix bugs, sanitize inputs * stepper: work on delay prediction improvements * stepper: json is mean, double quote are mandatory inside * mqtt: add details about message exchanged * imager: first implementation of json messages * main.json: add new tab for RPi management + json for payloads * imager: add state_machine class * stepper: publish last will * imager: major refactor * main: make streaming server process a daemon * mqtt: insert debug statement on close * main: reorder imports * imager: make it work! Reinsert the streaming server logic in there, because there is a problem with the synchronisation part otherwise. Also, eventually, StreamingOuput() will have to be made not global Final very critical learning: it's super duper important to make sure the memory split is at least 256Meg for the GPU. Chaos ensues otherwise * main: changes to accomodate the streamer/imager fusino * imager_state_machine: insert states transition description * stepper: cleanup of code * segmenter: creation of the class * python: include segmenter changes * remove unused files * stepper: check existence of hardware.json * main.json: changes to reflect the python script evolution * remove unecessary TODOs and add some others * main: add check for config and directories * imager: update_config is implemented and we have better management of directories now * segmenter: now work recursively in all folders * flow: the configuration is now sent via mqtt * segmenter: better manage pipeline error * segmenter: declaration of archive_fn in init * imager: small bugs and typos * main: add uniqueID output * imager: add the camera settings message We can now update the ISO, shutter speed and resolution from Node-Red * package.json: update dependencies
2020-09-28 11:05:27 +02:00
"name": "",
2021-12-03 02:34:14 +01:00
"events": "change",
"x": 560,
"y": 1140,
2020-11-24 17:25:15 +01:00
"wires": [
2020-11-25 16:58:32 +01:00
[]
2020-11-24 17:25:15 +01:00
]
2020-10-06 17:22:25 +02:00
},
2021-09-06 18:39:52 +02:00
{
2021-12-03 02:34:14 +01:00
"id": "902429eb.ceacb8",
"type": "ui_toast",
"z": "bccd1f23.87219",
"position": "dialog",
"displayTime": "3",
"highlight": "",
"sendall": true,
2021-09-06 18:39:52 +02:00
"outputs": 1,
2021-12-03 02:34:14 +01:00
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
2020-11-25 16:58:32 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"x": 570,
"y": 1220,
2020-10-06 17:22:25 +02:00
"wires": [
2021-12-03 02:34:14 +01:00
[]
2020-10-06 17:22:25 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "fe840e05.b46f3",
2020-11-24 17:25:15 +01:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "bccd1f23.87219",
"name": "Check form",
"func": "var acq_fnumber_objective= global.get(\"acq_fnumber_objective\");\n\nif (acq_fnumber_objective === undefined || acq_fnumber_objective === \"\") {\n msg.topic = \"Missing entry :\";\n msg.payload = \"Focal Length of the objective\";\n return [null, msg];\n}\n\nmsg.topic = \"Change Tab\";\nmsg.payload={\"tab\":\"Fluidic Acquisition\"};\nreturn [msg, null];",
"outputs": 2,
2020-11-24 17:25:15 +01:00
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 330,
"y": 1180,
2020-10-06 17:22:25 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"6c792043.b6ff9",
"326a1d95.ca21aa"
],
[
"902429eb.ceacb8"
2020-10-06 17:22:25 +02:00
]
2021-12-03 02:34:14 +01:00
],
"outputLabels": [
"message",
"error"
2020-10-06 17:22:25 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "5846b1d4.7971b",
"type": "ui_button",
"z": "bccd1f23.87219",
"name": "Backward",
"group": "707d9797.c8e798",
"order": 2,
"width": 1,
"height": 1,
"passthru": false,
"label": "",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "fa-rotate-left fa-2x",
"payload": "BACKWARD",
"payloadType": "str",
"topic": "actuator/pump",
"x": 560,
"y": 280,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"3cb96380.e575ec"
2020-11-24 17:25:15 +01:00
]
]
2020-10-06 17:22:25 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "2cab680b.baf888",
"type": "ui_button",
"z": "bccd1f23.87219",
"name": "Forward",
"group": "707d9797.c8e798",
"order": 4,
"width": 1,
"height": 1,
"passthru": true,
"label": "",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "fa-rotate-right fa-2x",
"payload": "FORWARD",
"payloadType": "str",
"topic": "actuator/pump",
"x": 560,
"y": 320,
2020-10-06 17:22:25 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"3cb96380.e575ec"
2020-10-06 17:22:25 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "9b515beb.4aea48",
"type": "ui_template",
"z": "bccd1f23.87219",
"group": "4248342d.e55fac",
"name": "Magnification",
"order": 4,
"width": 0,
"height": 0,
"format": "<div>\n Magnification : X\n <span id=\"obj_magnification\" ng-bind-html=\"msg.payload\">\n </span>\n</div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": false,
"templateScope": "local",
2021-12-03 02:51:25 +01:00
"x": 1330,
2021-12-03 02:34:14 +01:00
"y": 80,
"wires": [
[]
]
},
{
"id": "8038414a.34461",
2020-11-25 16:58:32 +01:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "bccd1f23.87219",
"name": "Calculate optics",
"func": "var acq_fnumber_objective = String(global.get(\"acq_fnumber_objective\"));\nvar acq_camera = global.get(\"acq_camera\")\n\n// Those values needs to be recalculated, they are not good!\nif (acq_camera == \"HQ Camera\"){\n switch(acq_fnumber_objective) {\n case \"25\":\n acq_magnification= 0.6;\n process_pixel= 1.56;\n sug_min= 60;\n sug_max= 670;\n sug_flowrate= 3;\n break;\n case \"16\":\n acq_magnification= 1.6;\n process_pixel= 1.01;\n sug_min= 40;\n sug_max= 430;\n sug_flowrate= 2.4;\n break;\n case \"12\":\n acq_magnification= 1.20;\n process_pixel= 0.79;\n sug_min= 30;\n sug_max= 340;\n sug_flowrate= 1.25;\n break;\n case \"8\":\n acq_magnification= 1.78;\n process_pixel= 0.53;\n sug_min= 20;\n sug_max= 230;\n sug_flowrate= 0.42;\n break;\n case \"6\":\n acq_magnification= 2.36;\n process_pixel= 0.41;\n sug_min= 15;\n sug_max= 170;\n sug_flowrate= 0.32;\n break;\n }\n}\nelse if (acq_camera == \"Camera v2.1\"){\n switch(acq_fnumber_objective) {\n case \"25\":\n acq_magnification= 0.6;\n process_pixel= 1.86;\n sug_min= 60;\n sug_max= 670;\n sug_flowrate= 3;\n break;\n case \"16\":\n acq_magnification= 1.6;\n process_pixel= 0.7;\n sug_min= 40;\n sug_max= 430;\n sug_flowrate= 2.4;\n break;\n case \"12\":\n acq_magnification= 1.20;\n process_pixel= 0.94;\n sug_min= 30;\n sug_max= 340;\n sug_flowrate= 1.25;\n break;\n case \"8\":\n acq_magnification= 1.78;\n process_pixel= 0.63;\n sug_min= 20;\n sug_max= 230;\n sug_flowrate= 0.42;\n break;\n case \"6\":\n acq_magnification= 2.36;\n process_pixel= 0.48;\n sug_min= 15;\n sug_max= 170;\n sug_flowrate= 0.32;\n break;\n }\n}else {\n acq_magnification= \"ERROR\";\n process_pixel= \"ERROR\";\n sug_min= \"ERROR\";\n sug_max= \"ERROR\";\n sug_flowrate= \"ERROR\";\n}\n\nvar process_pixel_fixed = global.get(\"process_pixel_fixed\")\n\nif (process_pixel_fixed !== undefined && process_pixel_fixed !== \"\") {\n process_pixel = process_pixel_fixed\n}\n\nglobal.set(\"process_pixel\",process_pixel);\n\nglobal.set(\"acq_magnification\",acq_magnification);\n\n\nreturn [{payload: acq_fnumber_objective}, {payload: acq_magnification}, {payload: process_pixel}, {payload: sug_min}, {payload: sug_max}];",
"outputs": 5,
2020-11-25 16:58:32 +01:00
"noerr": 0,
2021-12-03 02:34:14 +01:00
"initialize": "",
"finalize": "",
"libs": [],
"x": 1080,
"y": 120,
2020-10-06 17:22:25 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"335a730b9a3830ff"
],
[
"9b515beb.4aea48"
],
[
"3d2360ad.e41e7"
],
[
"8e66977b.9166a8"
],
[
"26b32f2c.c64fc"
2020-10-06 17:22:25 +02:00
]
2021-12-03 02:34:14 +01:00
],
"inputLabels": [
"acq_fnumber_objective"
],
"outputLabels": [
"acq_magnification",
"process_pixel",
"sug_min",
"sug_max",
""
2020-10-06 17:22:25 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "3d2360ad.e41e7",
"type": "ui_template",
"z": "bccd1f23.87219",
"group": "4248342d.e55fac",
"name": "process_pixel",
"order": 5,
2020-11-25 16:58:32 +01:00
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"format": "<div>\n Pixel resolution :\n <span id=\"process_pixel\" ng-bind-html=\"msg.payload\"></span>\n μm\n</div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": false,
"templateScope": "local",
"x": 1340,
"y": 120,
"wires": [
[]
]
2020-11-25 16:58:32 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "8e66977b.9166a8",
"type": "ui_template",
"z": "bccd1f23.87219",
"group": "4248342d.e55fac",
"name": "min_size",
"order": 6,
2020-11-25 16:58:32 +01:00
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"format": "<div>\n Smallest cells to explore :\n <span id=\"min_size\" ng-bind-html=\"msg.payload\"></span>\n μm\n</div>",
"storeOutMessages": true,
"fwdInMessages": true,
"templateScope": "local",
"x": 1320,
"y": 160,
"wires": [
[]
]
2020-11-25 16:58:32 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "26b32f2c.c64fc",
"type": "ui_template",
"z": "bccd1f23.87219",
"group": "4248342d.e55fac",
"name": "max_size",
"order": 7,
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"format": "<div>\n Biggest cells to explore :\n <span id=\"max_size\" ng-bind-html=\"msg.payload\"></span>\n μm\n</div>",
"storeOutMessages": true,
"fwdInMessages": true,
"templateScope": "local",
"x": 1320,
"y": 200,
"wires": [
[]
]
},
2020-11-25 16:58:32 +01:00
{
2021-12-03 02:34:14 +01:00
"id": "f61aaed5.1e64",
"type": "ui_button",
"z": "bccd1f23.87219",
2020-11-25 16:58:32 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"group": "7a0b4877.a5d268",
2021-01-15 11:48:27 +01:00
"order": 1,
2021-12-03 02:34:14 +01:00
"width": 2,
2020-11-25 16:58:32 +01:00
"height": 1,
2021-12-03 02:34:14 +01:00
"passthru": false,
"label": "Previous",
"tooltip": "",
"color": "#097479",
"bgcolor": "white",
"icon": "keyboard_return",
"payload": "{\"tab\":\"Sample\"}",
"payloadType": "json",
"topic": "",
"x": 140,
"y": 1140,
"wires": [
[
"6c792043.b6ff9"
]
]
2020-11-25 16:58:32 +01:00
},
2020-11-27 11:30:57 +01:00
{
2021-12-03 02:34:14 +01:00
"id": "9ba6ec0a.22c96",
2020-11-27 11:30:57 +01:00
"type": "ui_button",
"z": "bccd1f23.87219",
2021-12-03 02:34:14 +01:00
"name": "",
"group": "7a0b4877.a5d268",
"order": 2,
"width": 2,
2020-11-27 11:30:57 +01:00
"height": 1,
2021-12-03 02:34:14 +01:00
"passthru": false,
"label": "Continue",
2020-11-27 11:30:57 +01:00
"tooltip": "",
2021-12-03 02:34:14 +01:00
"color": "#097479",
"bgcolor": "white",
"icon": "keyboard_tab",
"payload": "{\"tab\":\"Home\"}",
2020-11-27 11:30:57 +01:00
"payloadType": "json",
2021-12-03 02:34:14 +01:00
"topic": "",
"x": 140,
"y": 1180,
2020-10-06 17:22:25 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"fe840e05.b46f3"
2020-10-06 17:22:25 +02:00
]
]
},
2020-11-27 11:30:57 +01:00
{
2021-12-03 02:34:14 +01:00
"id": "cbb8afed.0a026",
"type": "rpi-gpio out",
"z": "bccd1f23.87219",
"name": "LED Output",
"pin": "21",
"set": true,
"level": "0",
"freq": "",
"out": "out",
"bcm": true,
"x": 550,
"y": 120,
"wires": []
},
{
"id": "3cb96380.e575ec",
"type": "function",
"z": "bccd1f23.87219",
"name": "pump",
"func": "var manual_volume= global.get(\"pump_manual_volume\");\nvar flowrate= global.get(\"pump_flowrate\");\n\nif (manual_volume === undefined || manual_volume === \"\" || manual_volume === null) {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Volume to pass\";\n return [null, msg];\n}\nelse if (flowrate === undefined || flowrate === \"\" || flowrate === null) {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Flowrate\";\n return [null, msg];\n}\nelse {\n msg.topic = \"actuator/pump\";\n // msg.payload is FORWARD or BACKWARD here\n msg.payload={\"action\":\"move\", \n \"direction\":msg.payload,\n \"volume\":manual_volume,\n \"flowrate\":flowrate};\n}\nreturn [msg, null];",
"outputs": 2,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 750,
"y": 300,
"wires": [
[
"bdc8ce57.de1f08"
],
[
"8bcce348.efc1a"
]
2020-11-25 16:58:32 +01:00
],
2021-12-03 02:34:14 +01:00
"inputLabels": [
"direction"
],
"outputLabels": [
"message",
"error"
]
},
{
"id": "8bcce348.efc1a",
"type": "ui_toast",
"z": "bccd1f23.87219",
"position": "dialog",
"displayTime": "3",
"highlight": "",
"sendall": true,
"outputs": 1,
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
"name": "",
"x": 1090,
"y": 300,
2020-10-06 17:22:25 +02:00
"wires": [
2021-12-03 02:34:14 +01:00
[]
2020-10-06 17:22:25 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "bdc8ce57.de1f08",
"type": "mqtt out",
"z": "bccd1f23.87219",
"name": "",
"topic": "",
"qos": "",
"retain": "",
"broker": "8dc3722c.06efa8",
"x": 1210,
"y": 260,
"wires": []
},
{
"id": "d71d224f.0585d8",
"type": "ui_toast",
"z": "bccd1f23.87219",
"position": "dialog",
"displayTime": "3",
"highlight": "",
"sendall": true,
2020-11-25 16:58:32 +01:00
"outputs": 1,
2021-12-03 02:34:14 +01:00
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
"name": "",
"x": 1090,
"y": 800,
2020-11-27 11:30:57 +01:00
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "65ad39d.b6d4d48",
"type": "function",
"z": "bccd1f23.87219",
"name": "focus",
"func": "var distance = global.get(\"focus_distance\");\n\nif (distance === undefined || distance === \"\" || distance === null) {\n msg.topic = \"Missing entry :\"\n msg.payload = \"Distance\";\n return [null, msg]\n}else {\n distance = global.get(\"focus_distance\");\n // msg.payload is UP or DOWN here\n msg.payload={\"action\":\"move\", \n \"direction\":msg.payload,\n \"distance\":(distance/1000)};\n}\nreturn [msg, null];",
"outputs": 2,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 850,
"y": 780,
2020-11-27 11:30:57 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"62030521.88317c"
],
[
"d71d224f.0585d8"
2020-11-27 11:30:57 +01:00
]
2021-12-03 02:34:14 +01:00
],
"inputLabels": [
"direction"
],
"outputLabels": [
"message",
"error"
2020-11-27 11:30:57 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "1962d999.4a97e6",
"type": "ui_button",
"z": "bccd1f23.87219",
"name": "stop focus",
"group": "fbd92986.1028c8",
2021-01-04 11:48:36 +01:00
"order": 9,
2021-12-03 02:34:14 +01:00
"width": 0,
"height": 0,
2020-11-27 11:30:57 +01:00
"passthru": true,
2021-12-03 02:34:14 +01:00
"label": " STOP FOCUS",
"tooltip": "",
"color": "",
"bgcolor": "#AD1625",
"icon": "fa-pause fa-2x",
"payload": "{\"action\":\"stop\"}",
"payloadType": "json",
"topic": "actuator/focus",
"x": 570,
"y": 560,
2020-11-27 11:30:57 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"62030521.88317c"
2020-11-27 11:30:57 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "62030521.88317c",
"type": "mqtt out",
"z": "bccd1f23.87219",
"name": "",
"topic": "",
"qos": "",
"retain": "",
"broker": "8dc3722c.06efa8",
"x": 1210,
"y": 720,
"wires": []
2020-10-06 17:22:25 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "3bd43039.bc5fb8",
"type": "ui_button",
"z": "bccd1f23.87219",
"name": "",
"group": "fbd92986.1028c8",
"order": 1,
"width": 4,
"height": 1,
"passthru": false,
"label": "UP 1mm",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "fa-angle-up fa-3x",
"payload": "{\"action\":\"move\",\"direction\":\"UP\",\"distance\":1}",
"payloadType": "json",
"topic": "actuator/focus",
"topicType": "str",
"x": 560,
"y": 600,
2020-10-06 17:22:25 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"62030521.88317c"
2020-10-06 17:22:25 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "c0663029.2d03b",
"type": "ui_button",
"z": "bccd1f23.87219",
"name": "",
"group": "fbd92986.1028c8",
"order": 8,
"width": 4,
2020-11-27 11:30:57 +01:00
"height": 1,
2021-12-03 02:34:14 +01:00
"passthru": false,
"label": "DOWN 1mm",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "fa-angle-down fa-3x",
"payload": "{\"action\":\"move\",\"direction\":\"DOWN\",\"distance\":1}",
"payloadType": "json",
"topic": "actuator/focus",
"topicType": "str",
"x": 570,
"y": 720,
"wires": [
[
"62030521.88317c"
]
]
2020-11-27 11:30:57 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "71f55a58.d7eaf4",
"type": "inject",
"z": "bccd1f23.87219",
"name": "Default: 500µm",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "0.5",
"topic": "",
"payload": "500",
"payloadType": "num",
"x": 120,
"y": 460,
"wires": [
[
"be33e564.029358"
]
]
2020-11-27 11:30:57 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "9a1d0e7c.2d5a1",
"type": "inject",
"z": "bccd1f23.87219",
"name": "Default: OFF",
"props": [
{
"p": "payload"
}
2020-11-25 16:58:32 +01:00
],
2021-12-03 02:34:14 +01:00
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "off",
"payloadType": "str",
"x": 130,
"y": 120,
2020-10-06 17:22:25 +02:00
"wires": [
2020-11-27 11:30:57 +01:00
[
2021-12-03 02:34:14 +01:00
"f0775525.cf806"
2020-11-27 11:30:57 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "f782a471.447748",
"type": "inject",
"z": "bccd1f23.87219",
"name": "Default: 2mL/min",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "0.5",
"topic": "",
"payload": "2",
"payloadType": "num",
"x": 130,
"y": 380,
2020-11-27 11:30:57 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"cb2d5174.cfe9f"
2020-11-27 11:30:57 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "73b8252a.5ca754",
"type": "inject",
"z": "bccd1f23.87219",
"name": "Default: 2mL",
"props": [
2020-11-27 11:30:57 +01:00
{
2021-12-03 02:34:14 +01:00
"p": "payload"
2020-11-27 11:30:57 +01:00
}
],
2021-12-03 02:34:14 +01:00
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "0.5",
"topic": "",
"payload": "2",
"payloadType": "num",
"x": 110,
"y": 420,
2020-11-27 11:30:57 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"68962547.34a67c"
2020-11-27 11:30:57 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6451f991.aaac1",
"type": "ui_button",
"z": "bccd1f23.87219",
"name": "stop pump",
"group": "707d9797.c8e798",
"order": 5,
"width": 4,
"height": 1,
"passthru": true,
"label": " STOP PUMP",
"tooltip": "",
"color": "",
"bgcolor": "#AD1625",
"icon": "fa-pause fa-2x",
"payload": "{\"action\":\"stop\"}",
"payloadType": "json",
"topic": "actuator/pump",
"x": 570,
"y": 240,
2020-11-27 11:30:57 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"bdc8ce57.de1f08"
2020-11-27 11:30:57 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "cfc783d7.d6ceb",
"type": "link in",
"z": "bccd1f23.87219",
"name": "Optics recalculation",
2020-11-27 11:30:57 +01:00
"links": [
2021-12-03 02:34:14 +01:00
"559a8085.1d6b9",
"5d5ad36d2c50dcc2"
2020-11-27 11:30:57 +01:00
],
2021-12-03 02:34:14 +01:00
"x": 915,
"y": 120,
"wires": [
[
"8038414a.34461"
]
]
2020-11-27 11:30:57 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "326a1d95.ca21aa",
"type": "subflow:1c24ad9c.bebec2",
"z": "bccd1f23.87219",
"name": "",
"x": 550,
"y": 1180,
"wires": [
[]
]
},
{
"id": "2d371e59.b0e50a",
"type": "inject",
"z": "bccd1f23.87219",
"name": "Default: 1000µm/s",
"props": [
2020-11-27 11:30:57 +01:00
{
2021-12-03 02:34:14 +01:00
"p": "payload"
2020-11-27 11:30:57 +01:00
}
],
2021-12-03 02:34:14 +01:00
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "0.5",
"topic": "",
"payload": "1000",
"payloadType": "num",
"x": 130,
"y": 500,
2020-11-27 11:30:57 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"3a86de51.765b9a"
2020-11-27 11:30:57 +01:00
]
2020-11-24 17:25:15 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "167cda35.c9b6ae",
"type": "ui_button",
"z": "bccd1f23.87219",
"name": "",
"group": "fbd92986.1028c8",
"order": 2,
"width": 4,
"height": 1,
"passthru": false,
"label": "UP 100um",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "fa-angle-up fa-3x",
"payload": "{\"action\":\"move\",\"direction\":\"UP\",\"distance\":0.1}",
"payloadType": "json",
"topic": "actuator/focus",
"topicType": "str",
"x": 570,
"y": 640,
2020-11-24 17:25:15 +01:00
"wires": [
2020-11-25 16:58:32 +01:00
[
2021-12-03 02:34:14 +01:00
"62030521.88317c"
2020-11-25 16:58:32 +01:00
]
2020-11-24 17:25:15 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "72a7c597.9374fc",
"type": "ui_button",
"z": "bccd1f23.87219",
2020-11-25 16:58:32 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"group": "fbd92986.1028c8",
"order": 7,
"width": 4,
"height": 1,
"passthru": false,
"label": "DOWN 100um",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "fa-angle-down fa-3x",
"payload": "{\"action\":\"move\",\"direction\":\"DOWN\",\"distance\":0.1}",
"payloadType": "json",
"topic": "actuator/focus",
"topicType": "str",
"x": 580,
"y": 680,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"62030521.88317c"
2020-11-24 17:25:15 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "f0775525.cf806",
"type": "ui_multistate_switch",
"z": "bccd1f23.87219",
"name": "light_control",
"group": "4248342d.e55fac",
"order": 1,
"width": 5,
"height": 1,
"label": "Light ",
"stateField": "payload",
"enableField": "enable",
"rounded": true,
"useThemeColors": true,
"hideSelectedLabel": false,
"options": [
2020-11-25 16:58:32 +01:00
{
2021-12-03 02:34:14 +01:00
"label": "Off",
"value": "0",
"valueType": "num",
"color": "#009933"
},
{
"label": "On",
"value": "1",
"valueType": "num",
"color": "#999999"
2020-11-25 16:58:32 +01:00
}
],
2021-12-03 02:34:14 +01:00
"x": 350,
"y": 120,
2020-11-24 17:25:15 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"cbb8afed.0a026"
2020-11-24 17:25:15 +01:00
]
]
},
2020-11-25 16:58:32 +01:00
{
2021-12-03 02:34:14 +01:00
"id": "8ea9dc9a.c7d87",
2020-11-25 16:58:32 +01:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "bccd1f23.87219",
"name": "Encapsulate settings",
"func": "msg.payload = {\n \"action\":\"settings\", \n \"settings\":{[msg.topic]:msg.payload}\n}\n\nmsg.topic = \"imager/image\"\nreturn msg;",
2020-11-25 16:58:32 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 940,
"y": 940,
"wires": [
[
2021-12-03 02:34:14 +01:00
"845e06e1.0d812"
2020-11-27 11:30:57 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "5765a825.a595c8",
"type": "ui_slider",
"z": "bccd1f23.87219",
"name": "Shutter speed slider",
"label": "Shutter Speed",
"tooltip": "In microseconds, up to 1000µs, 125µs by default",
"group": "8c38a81e.9897a8",
"order": 4,
"width": 0,
"height": 0,
"passthru": true,
"outs": "end",
"topic": "shutter_speed",
"topicType": "str",
"min": "125",
"max": "1000",
"step": "1",
"className": "",
2021-12-03 02:34:14 +01:00
"x": 600,
"y": 880,
2020-11-27 11:30:57 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"8ea9dc9a.c7d87"
2020-11-27 11:30:57 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "845e06e1.0d812",
"type": "mqtt out",
"z": "bccd1f23.87219",
"name": "",
"topic": "",
"qos": "",
"retain": "",
"broker": "8dc3722c.06efa8",
"x": 1210,
"y": 980,
"wires": []
},
{
"id": "2350e507.d4e302",
"type": "inject",
"z": "bccd1f23.87219",
"name": "Default: 125µs",
"props": [
{
"p": "payload"
}
2020-11-27 11:30:57 +01:00
],
2021-12-03 02:34:14 +01:00
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "1",
"topic": "",
"payload": "125",
"payloadType": "num",
"x": 120,
"y": 880,
2020-11-27 11:30:57 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"5765a825.a595c8"
]
2020-11-27 11:30:57 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "5e147425.7666ec",
2020-11-27 11:30:57 +01:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "bccd1f23.87219",
"name": "Encapsulate wb gain settings",
"func": "msg.payload = {\n \"action\":\"settings\", \n \"settings\":{\"white_balance_gain\":{\n [msg.topic]:Math.round(msg.payload*100)\n }\n }\n}\nmsg.topic = \"imager/image\"\nreturn msg;",
2020-11-27 11:30:57 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"libs": [],
2021-12-03 02:51:25 +01:00
"x": 920,
2021-12-03 02:34:14 +01:00
"y": 1060,
2020-11-27 11:30:57 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"845e06e1.0d812"
2020-11-27 11:30:57 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "82722a3c.846b3",
"type": "inject",
"z": "bccd1f23.87219",
"name": "Default: OFF",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "1",
2020-11-27 11:30:57 +01:00
"topic": "",
2021-12-03 02:34:14 +01:00
"payload": "false",
"payloadType": "bool",
"x": 110,
"y": 920,
"wires": [
[
"60e44330.50bdec"
]
]
2020-11-27 11:30:57 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "60e44330.50bdec",
"type": "ui_switch",
"z": "bccd1f23.87219",
"name": "AWB",
"label": "Auto White Balance",
"tooltip": "",
"group": "8c38a81e.9897a8",
"order": 5,
"width": 2,
"height": 2,
"passthru": true,
"decouple": "false",
"topic": "white_balance",
"topicType": "str",
"style": "",
"onvalue": "auto",
"onvalueType": "str",
"onicon": "",
"oncolor": "",
"offvalue": "off",
"offvalueType": "str",
"officon": "",
"offcolor": "",
"animate": true,
"className": "",
2021-12-03 02:34:14 +01:00
"x": 550,
"y": 920,
2020-11-27 11:30:57 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"8ea9dc9a.c7d87"
]
2020-11-27 11:30:57 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "c3cdf9e.1d27308",
"type": "subflow:4ed26b8b.253504",
"z": "bccd1f23.87219",
"name": "",
"env": [],
"x": 120,
"y": 1020,
"wires": [
[
"6be64480.7e7e24",
"6d49d161.13628",
"8ef294ba.12213",
"8e336e58.14722"
]
]
2020-11-27 11:30:57 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "6be64480.7e7e24",
"type": "change",
"z": "bccd1f23.87219",
"name": "Get red_gain",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload.red_gain",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 330,
"y": 1040,
2020-11-27 11:30:57 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"d5415af6.e06cc"
]
]
2020-11-24 17:25:15 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "6d49d161.13628",
2020-11-25 16:58:32 +01:00
"type": "change",
2021-12-03 02:34:14 +01:00
"z": "bccd1f23.87219",
"name": "Get blue_gain",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload.blue_gain",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 340,
"y": 1080,
"wires": [
[
"dba68c1f.e3144"
]
]
},
{
"id": "56835fa1.2fe538",
"type": "subflow:4ed26b8b.253504",
"z": "bccd1f23.87219",
"name": "",
"env": [],
"x": 960,
"y": 1020,
"wires": [
[]
]
},
{
"id": "8ef294ba.12213",
"type": "change",
"z": "bccd1f23.87219",
"name": "Get analog_gain",
2020-11-25 16:58:32 +01:00
"rules": [
{
2021-12-03 02:34:14 +01:00
"t": "set",
"p": "payload",
2020-11-27 11:30:57 +01:00
"pt": "msg",
2021-12-03 02:34:14 +01:00
"to": "1",
"tot": "num"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload.analog_gain",
"tot": "msg"
2020-11-25 16:58:32 +01:00
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
2021-12-03 02:51:25 +01:00
"x": 340,
2021-12-03 02:34:14 +01:00
"y": 960,
2020-11-24 17:25:15 +01:00
"wires": [
2020-11-27 11:30:57 +01:00
[
2021-12-03 02:34:14 +01:00
"a6c7eec4.f7a918"
2020-11-27 11:30:57 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "8e336e58.14722",
"type": "change",
"z": "bccd1f23.87219",
"name": "Get digital_gain",
"rules": [
2020-11-27 11:30:57 +01:00
{
2021-12-03 02:34:14 +01:00
"t": "set",
"p": "payload",
"pt": "msg",
"to": "1",
"tot": "num"
2020-11-27 11:30:57 +01:00
},
{
2021-12-03 02:34:14 +01:00
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload.digital_gain",
"tot": "msg"
2020-11-27 11:30:57 +01:00
}
],
2021-12-03 02:34:14 +01:00
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 340,
"y": 1000,
2020-11-27 11:30:57 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"66b37eef.f3f9e"
2020-11-27 11:30:57 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "a6c7eec4.f7a918",
"type": "ui_text_input",
"z": "bccd1f23.87219",
"d": true,
"name": "Analog gain field",
"label": "Analog Gain",
"tooltip": "From 1.0 to 12.0",
"group": "8c38a81e.9897a8",
"order": 1,
"width": 0,
"height": 0,
"passthru": true,
"mode": "number",
"delay": 300,
"topic": "analog",
"topicType": "str",
2021-12-03 02:51:25 +01:00
"x": 580,
2021-12-03 02:34:14 +01:00
"y": 960,
2020-11-27 11:30:57 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"44e02933.a66688",
"d361a2c4.0990f8"
2020-11-27 11:30:57 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "66b37eef.f3f9e",
"type": "ui_text_input",
"z": "bccd1f23.87219",
"d": true,
"name": "Digital gain field",
"label": "Digital Gain",
"tooltip": "From 1.0 to 64.0. Overexpose starting at 4.0",
"group": "8c38a81e.9897a8",
"order": 2,
"width": 0,
"height": 0,
"passthru": true,
"mode": "number",
"delay": 300,
"topic": "digital",
"topicType": "str",
"x": 580,
"y": 1000,
2020-11-27 18:57:43 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"44e02933.a66688",
"d361a2c4.0990f8"
2020-11-27 18:57:43 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "44e02933.a66688",
2020-11-27 18:57:43 +01:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "bccd1f23.87219",
"name": "Encapsulate image gain settings",
"func": "msg.payload = {\n \"action\":\"settings\", \n \"settings\":{\"image_gain\":{\n [msg.topic]:Math.round(msg.payload*100)\n }\n }\n}\nmsg.topic = \"imager/image\"\nreturn msg;",
2020-11-27 18:57:43 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:51:25 +01:00
"x": 910,
2021-12-03 02:34:14 +01:00
"y": 980,
2020-11-27 18:57:43 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"845e06e1.0d812"
2020-11-27 18:57:43 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d361a2c4.0990f8",
"type": "change",
"z": "bccd1f23.87219",
"name": "topic *_gain",
"rules": [
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "topic&'_gain'",
"tot": "jsonata"
}
2020-11-27 18:57:43 +01:00
],
2021-12-03 02:34:14 +01:00
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 790,
"y": 1020,
2020-11-27 18:57:43 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"56835fa1.2fe538"
2020-11-27 18:57:43 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d8d006bf.2947f",
"type": "inject",
"z": "bccd1f23.87219",
"name": "Default: ISO 100",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "1",
"topic": "",
"payload": "100",
"payloadType": "num",
"x": 130,
"y": 840,
"wires": [
2021-12-03 02:34:14 +01:00
[
"9feb06fb.43b558"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "9feb06fb.43b558",
"type": "ui_multistate_switch",
"z": "bccd1f23.87219",
"name": "ISO selector",
"group": "8c38a81e.9897a8",
"order": 3,
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"label": "ISO",
"stateField": "payload",
"enableField": "enable",
"passthroughField": "passthrough",
"inputMsgField": "inputmsg",
2021-12-03 02:34:14 +01:00
"rounded": true,
"useThemeColors": true,
"hideSelectedLabel": false,
"multilineLabel": false,
"passThrough": "never",
"inputMsg": "all",
"userInput": "enabled_show",
2021-12-03 02:34:14 +01:00
"options": [
{
"label": "100",
"value": "100",
"valueType": "num",
"color": "#009933"
},
{
"label": "200",
"value": "200",
"valueType": "num",
"color": "#999999"
},
{
"label": "320",
"value": "320",
"valueType": "num",
"color": "#ff6666"
},
{
"label": "400",
"value": "400",
"valueType": "num",
"color": "#009999"
},
{
"label": "500",
"value": "500",
"valueType": "num",
"color": "#cccc00"
},
{
"label": "640",
"value": "640",
"valueType": "num",
"color": "#ff33cc"
},
{
"label": "800",
"value": "800",
"valueType": "num",
"color": "#cc6600"
}
],
"x": 570,
"y": 840,
"wires": [
2021-12-03 02:34:14 +01:00
[
"eb9966de.cf13c8"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "eb9966de.cf13c8",
"type": "change",
"z": "bccd1f23.87219",
"name": "",
"rules": [
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "iso",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 740,
"y": 840,
"wires": [
2021-12-03 02:34:14 +01:00
[
"8ea9dc9a.c7d87"
]
]
2020-11-29 14:28:31 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "be33e564.029358",
"type": "ui_text_input",
"z": "bccd1f23.87219",
"name": "focus_distance",
"label": "Focus Distance (in µm)",
"tooltip": "in µm, 25µm resolution",
"group": "fbd92986.1028c8",
"order": 4,
2021-12-03 02:34:14 +01:00
"width": 0,
"height": 0,
2020-11-29 14:28:31 +01:00
"passthru": true,
2021-12-03 02:34:14 +01:00
"mode": "number",
"delay": 300,
"topic": "focus_distance",
"sendOnBlur": true,
"className": "",
2021-12-03 02:34:14 +01:00
"topicType": "str",
"x": 580,
"y": 460,
2020-11-29 14:28:31 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"dc48dc42.98d18"
2020-11-29 14:28:31 +01:00
]
]
2020-11-29 18:57:18 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "3a86de51.765b9a",
"type": "ui_text_input",
"z": "bccd1f23.87219",
"name": "focus_speed",
"label": "Focus Speed (in µm/sec)",
"tooltip": "in µm/sec",
"group": "fbd92986.1028c8",
"order": 5,
"width": 0,
"height": 0,
"passthru": true,
"mode": "number",
"delay": 300,
"topic": "focus_speed",
"sendOnBlur": true,
"className": "",
2021-12-03 02:34:14 +01:00
"topicType": "str",
"x": 570,
"y": 500,
2020-11-29 18:57:18 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"dc48dc42.98d18"
]
2020-11-29 18:57:18 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "cb2d5174.cfe9f",
"type": "ui_text_input",
"z": "bccd1f23.87219",
"name": "pump_flowrate",
"label": "Flowrate (ml/min)*",
"tooltip": "",
"group": "707d9797.c8e798",
"order": 1,
"width": 0,
"height": 0,
"passthru": true,
"mode": "text",
"delay": 300,
"topic": "pump_flowrate",
"topicType": "str",
"x": 580,
"y": 380,
2020-11-29 18:57:18 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"dc48dc42.98d18"
2020-11-29 18:57:18 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d5415af6.e06cc",
"type": "ui_text_input",
2021-12-03 02:34:14 +01:00
"z": "bccd1f23.87219",
"name": "WB Red input",
"label": "WB: Red",
"tooltip": "From 1.0 to 8.0",
"group": "8c38a81e.9897a8",
"order": 6,
"width": 4,
"height": 1,
"passthru": true,
"mode": "number",
2021-12-03 02:34:14 +01:00
"delay": 300,
"topic": "red",
"sendOnBlur": true,
"className": "",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"x": 580,
"y": 1040,
"wires": [
[
2021-12-03 02:34:14 +01:00
"d361a2c4.0990f8",
"5e147425.7666ec"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "dba68c1f.e3144",
"type": "ui_text_input",
2021-12-03 02:34:14 +01:00
"z": "bccd1f23.87219",
"name": "WB Blue input",
"label": "WB: Blue",
"tooltip": "From 1.0 to 64.0",
"group": "8c38a81e.9897a8",
"order": 7,
"width": 4,
"height": 1,
"passthru": true,
"mode": "number",
2021-12-03 02:34:14 +01:00
"delay": 300,
"topic": "blue",
"sendOnBlur": true,
"className": "",
2021-12-03 02:34:14 +01:00
"topicType": "str",
"x": 580,
"y": 1080,
"wires": [
[
2021-12-03 02:34:14 +01:00
"d361a2c4.0990f8",
"5e147425.7666ec"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "335a730b9a3830ff",
"type": "ui_template",
2021-12-03 02:34:14 +01:00
"z": "bccd1f23.87219",
"group": "4248342d.e55fac",
"name": "Lens",
"order": 3,
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"format": "<div>\n Objective lens aperture: \n <span id=\"obj_aperture\" ng-bind-html=\"msg.payload\">\n </span>mm\n</div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": false,
"templateScope": "local",
2021-12-03 02:34:14 +01:00
"x": 1310,
"y": 40,
"wires": [
[]
]
2020-12-01 21:12:29 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "142bc61a0bde1ba9",
"type": "link in",
"z": "bccd1f23.87219",
"name": "Cam Stream",
"links": [
"49111a72f4e32740"
2020-12-01 21:12:29 +01:00
],
2021-12-03 02:34:14 +01:00
"x": 235,
"y": 40,
2020-12-01 21:12:29 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"3ca8be38ddb269e9"
2020-12-01 21:12:29 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "3ca8be38ddb269e9",
"type": "template",
"z": "bccd1f23.87219",
"name": "Prepare stream HTML",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "<center>\n <img src=\"http://{{payload}}:8000/stream.mjpg\"\n onerror=\"this.onerror=null; this.src='http://{{payload}}:80/error.svg'\"\n alt=\"If you see this, there probably is an error either with your camera or with the python service. Please restart your machine.\"\n width=\"100%\" height=\"100%\">\n</center>",
"output": "str",
"x": 380,
"y": 40,
2020-12-01 21:12:29 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"6a84252a.d52a0c"
]
2020-12-01 21:12:29 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "f59a2f0d.5e9af",
"type": "ui_numeric",
"z": "baa1e3d9.cb29d",
"name": "acq_minimum_mesh",
"label": "Min fraction size (μm)",
"tooltip": "",
"group": "404c301a.19c4e",
"order": 1,
"width": 5,
"height": 1,
"wrap": false,
"passthru": true,
"topic": "acq_minimum_mesh",
"format": "{{value}}",
"min": 0,
"max": "300",
"step": "10",
"x": 640,
"y": 160,
2020-12-01 21:12:29 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"fb887036.12429"
2020-12-01 21:12:29 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6008a8bb.259f08",
"type": "ui_numeric",
"z": "baa1e3d9.cb29d",
2021-12-03 02:34:14 +01:00
"name": "acq_maximum_mesh",
"label": "Max fraction size (μm)",
"tooltip": "",
"group": "404c301a.19c4e",
"order": 2,
"width": 5,
"height": 1,
"wrap": false,
2020-12-09 14:52:12 +01:00
"passthru": true,
2021-12-03 02:34:14 +01:00
"topic": "acq_maximum_mesh",
"format": "{{value}}",
"min": "200",
"max": "2000",
"step": "100",
2021-12-01 13:30:23 +01:00
"x": 640,
2021-12-03 02:34:14 +01:00
"y": 200,
2020-12-01 21:12:29 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"fb887036.12429"
]
2020-12-01 21:12:29 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6b34c456.83178c",
"type": "ui_text_input",
"z": "baa1e3d9.cb29d",
2021-12-03 02:34:14 +01:00
"name": "acq_id",
"label": "Acquisition unique ID*",
"tooltip": "",
"group": "4322c187.e73e5",
"order": 1,
"width": 5,
"height": 1,
"passthru": true,
"mode": "text",
"delay": 300,
"topic": "acq_id",
"sendOnBlur": true,
"className": "",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"x": 690,
"y": 240,
2020-12-01 21:12:29 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"fb887036.12429"
]
2020-12-01 21:12:29 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "cc0ca68b.4263a8",
"type": "ui_dropdown",
"z": "baa1e3d9.cb29d",
"name": "acq_celltype",
"label": "Flowcell thickness*",
"tooltip": "",
"place": "Select option",
"group": "4322c187.e73e5",
"order": 6,
"width": 5,
"height": 1,
"passthru": true,
"multiple": false,
"options": [
{
"label": "200 μm µ-Slide I Luer",
"value": 200,
"type": "num"
},
{
"label": "300 µm capillary",
"value": 300,
"type": "num"
},
{
"label": "400 μm µ-Slide I Luer",
"value": 400,
"type": "num"
},
{
"label": "600 μm µ-Slide I Luer",
"value": 600,
"type": "num"
},
{
"label": "800 μm µ-Slide I Luer",
"value": 800,
"type": "num"
}
],
2021-12-03 02:34:14 +01:00
"payload": "",
"topic": "acq_celltype",
"topicType": "str",
"className": "",
2021-12-03 02:34:14 +01:00
"x": 670,
"y": 80,
2020-12-01 21:12:29 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"fb887036.12429",
"99b11fe4.2795d"
2020-12-01 21:12:29 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "fb887036.12429",
2020-12-01 21:12:29 +01:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "baa1e3d9.cb29d",
"name": "set global",
"func": "global.set(msg.topic, msg.payload);\nreturn msg",
2020-12-01 21:12:29 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
2021-12-03 02:34:14 +01:00
"x": 920,
"y": 220,
2020-12-01 21:12:29 +01:00
"wires": [
[
"52ea7d01.711034"
]
2020-12-01 21:12:29 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "bb2bb7ce.1d1458",
"type": "ui_template",
"z": "baa1e3d9.cb29d",
"group": "858a0e3c.987fe",
"name": "Stream Pi Camera",
"order": 1,
"width": 10,
"height": 8,
"format": "<span ng-bind-html=\"msg.payload\"></span>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": false,
"templateScope": "local",
"x": 650,
"y": 20,
2020-12-01 21:12:29 +01:00
"wires": [
[]
2020-12-01 21:12:29 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d0c5b57d.590818",
"type": "ui_ui_control",
"z": "baa1e3d9.cb29d",
"name": "",
"events": "change",
"x": 1280,
"y": 660,
2020-12-01 21:12:29 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[]
2020-12-01 21:12:29 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "c72f8fae.23bd4",
"type": "ui_button",
"z": "baa1e3d9.cb29d",
2020-12-01 21:12:29 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"group": "b7919ae2.c01788",
"order": 1,
2021-12-03 02:34:14 +01:00
"width": 5,
"height": 1,
"passthru": false,
2021-12-03 02:34:14 +01:00
"label": "Previous",
"tooltip": "",
"color": "#097479",
"bgcolor": "white",
"icon": "keyboard_return",
"payload": "{\"tab\":\"Optic Configuration\"}",
"payloadType": "json",
"topic": "",
2021-12-03 02:34:14 +01:00
"x": 1140,
"y": 660,
"wires": [
[
"d0c5b57d.590818"
]
]
},
{
"id": "29be525e.0c87fe",
"type": "ui_template",
"z": "baa1e3d9.cb29d",
"group": "b5d61bc7.54fe48",
"name": "Show Metadata",
"order": 9,
"width": 0,
"height": 0,
"format": "<div>\n <table style=\"text-align: center; width:100%\">\n <tr style=\"vertical-align: top\">\n <td>\n <h2>Sample</h2>\n <p><strong>id:</strong> <span id=\"max_size\" ng-bind-html=\"msg.payload.config.sample_id\"></span></p>\n <p><strong>project:</strong>\n <span id=\"max_size\" ng-bind-html=\"msg.payload.config.sample_project\"></span></p>\n <p><strong>ship:</strong> <span id=\"max_size\" ng-bind-html=\"msg.payload.config.sample_ship\"></span></p>\n <p><strong>operator:</strong>\n <span id=\"max_size\" ng-bind-html=\"msg.payload.config.sample_operator\"></span></p>\n <p><strong>sampling gear:</strong>\n <span id=\"max_size\" ng-bind-html=\"msg.payload.config.sample_sampling_gear\"></span></p>\n <p><strong>concentrated volume:</strong>\n <span id=\"max_size\" ng-bind-html=\"msg.payload.config.sample_concentrated_sample_volume\"></span></p>\n <p ng-if=msg.payload.config.sample_gear_net_opening><strong>gear net opening:</strong>\n <span id=\"max_size\" ng-bind-html=\"msg.payload.config.sample_gear_net_opening\"></span></p>\n <p ng-if=msg.payload.config.sample_total_volume><strong>total volume filtered:</strong>\n <span id=\"max_size\" ng-bind-html=\"msg.payload.config.sample_total_volume\"></span></p>\n </td>\n <td>\n <h2>Acquisition</h2>\n <p><strong>id:</strong> <span id=\"max_size\" ng-bind-html=\"msg.payload.config.acq_id\"></span></p>\n <p><strong>instrument:</strong>\n <span id=\"max_size\" ng-bind-html=\"msg.payload.config.acq_instrument\"></span></p>\n <p><strong>instrument id:</strong>\n <span id=\"max_size\" ng-bind-html=\"msg.payload.config.acq_instrument_id\"></span></p>\n <p><strong>camera:</strong> <span id=\"max_size\" ng-bind-html=\"msg.payload.config.acq_camera\"></span></p>\n <p><strong>celltype:</strong> <span id=\"max_size\" ng-bind-html=\"msg.payload.config.acq_celltype\"></span>\n </p>\n <p><strong>minimum mesh:</strong>\n <span id=\"max_size\" ng-bind-html=\"msg.payload.config.acq_minimum_mesh\"></span></p>\n <p><strong>maximum mesh:</strong>\n <span id=\"max_size\" ng-bind-html=\"msg.payload.config.acq_maximum_mesh\"></span></p>\n <p><strong>min esd:</strong> <span id=\"max_size\" ng-bind-html=\"msg.payload.config.acq_min_esd\"></span>\n </p>\n <p><strong>max esd:</strong> <span id=\"max_size\" ng-bind-html=\"msg.payload.config.acq_max_esd\"></span>\n </p>\n <p><strong>volume:</strong> <span id=\"max_size\" ng-bind-html=\"msg.payload.config.acq_volume\"></span></p>\n <p><strong>magnification:</strong>\n <span id=\"max_size\" ng-bind-html=\"msg.payload.config.acq_magnification\"></span></p>\n <p><strong>fnumber objective:</strong>\n <span id=\"max_size\" ng-bind-html=\"msg.payload.config.acq_fnumber_objective\"></span></p>\n <p><strong>software:</strong> <span id=\"max_size\" ng-bind-html=\"msg.payload.config.acq_software\"></span>\n </p>\n </td>\n </tr>\n <tr style=\"vertical-align: top\">\n <td>\n <h2>Object</h2>\n <p><strong>latitude:</strong> <span id=\"max_size\" ng-bind-html=\"msg.payload.config.object_lat\"></span>\n </p>\n <p><strong>longitude:</strong> <span id=\"max_size\" ng-bind-html=\"msg.payload.config.object_lon\"></span>\n </p>\n <p ng-if=msg.payload.config.object_lat_end><strong>latitude end:</strong>\n <span id=\"max_size\" ng-bind-html=\"msg.payload.config.
2021-12-03 02:34:14 +01:00
"storeOutMessages": false,
"fwdInMessages": false,
"resendOnRefresh": false,
"templateScope": "local",
"className": "",
2021-12-08 18:00:50 +01:00
"x": 1180,
"y": 380,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
2020-12-01 21:12:29 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "4b489713.ccde5",
"type": "ui_button",
2021-12-03 02:34:14 +01:00
"z": "baa1e3d9.cb29d",
2020-12-01 21:12:29 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"group": "4322c187.e73e5",
"order": 12,
"width": 5,
"height": 1,
"passthru": false,
2021-12-03 02:34:14 +01:00
"label": "Start Acquisition",
"tooltip": "",
"color": "",
"bgcolor": "",
2021-12-03 02:34:14 +01:00
"icon": "camera",
"payload": "",
"payloadType": "str",
"topic": "imager/image",
"x": 460,
"y": 480,
"wires": [
[
2021-12-03 02:34:14 +01:00
"c9f510c0.7d1328"
]
]
},
2020-12-01 21:12:29 +01:00
{
2021-12-03 02:34:14 +01:00
"id": "c9f510c0.7d1328",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "baa1e3d9.cb29d",
"name": "Image control",
"func": "// Reset the number of images taken\nflow.set('img_counter', 0);\n\nvar acq_celltype = global.get(\"acq_celltype\");\nvar acq_minimum_mesh = global.get(\"acq_minimum_mesh\");\nvar acq_maximum_mesh = global.get(\"acq_maximum_mesh\");\nvar imaging_pump_volume = global.get(\"imaging_pump_volume\");\nvar acq_id = global.get(\"acq_id\");\nvar nb_frame = global.get(\"nb_frame\");\nvar pump_direction = global.get(\"pump_direction\");\nvar sleep_before = global.get(\"sleep_before\");\nvar object_date = global.get(\"object_date\");\n\nif (acq_celltype === undefined || acq_celltype === \"\") {\n msg.topic = \"Missing entry :\";\n msg.payload = \"Type of the flowcell\";\n return [null, msg];\n} else if (acq_minimum_mesh === undefined || acq_minimum_mesh === \"\") {\n msg.topic = \"Missing entry :\";\n msg.payload = \"Lower fraction size\";\n return [null, msg];\n} else if (acq_maximum_mesh === undefined || acq_maximum_mesh === \"\") {\n msg.topic = \"Missing entry :\";\n msg.payload = \"Upper fraction size\";\n return [null, msg];\n} else if (imaging_pump_volume === undefined || imaging_pump_volume === \"\" || imaging_pump_volume === null) {\n msg.topic = \"Missing entry :\";\n msg.payload = \"Volume inbetween images\";\n return [null, msg];\n} else if (acq_id === undefined || acq_id === \"\") {\n msg.topic = \"Missing entry :\";\n msg.payload = \"Acquisition ID\";\n return [null, msg];\n} else if (nb_frame === undefined || nb_frame === \"\" || nb_frame === null) {\n msg.topic = \"Missing entry :\";\n msg.payload = \"Number of image to save\";\n return [null, msg];\n} else if (pump_direction === undefined || pump_direction === \"\" || pump_direction === null) {\n msg.topic = \"Missing entry :\";\n msg.payload = \"Pump direction\";\n return [null, msg];\n} else if (sleep_before === undefined || sleep_before === \"\" || sleep_before === null) {\n msg.topic = \"Missing entry :\";\n msg.payload = \"Delay before image\";\n return [null, msg];\n}else if (object_date === undefined || object_date === \"\" || object_date === null) {\n msg.topic = \"Missing entry :\";\n msg.payload = \"Object date\";\n return [null, msg];\n}\n\nmsg.payload = {\n \"action\": \"image\",\n \"sleep\": sleep_before,\n \"pump_direction\": pump_direction,\n \"volume\": imaging_pump_volume,\n \"nb_frame\": nb_frame,\n}\nmsg.send = true\n\nreturn [msg, null];",
2021-12-03 02:34:14 +01:00
"outputs": 2,
"noerr": 0,
2020-12-08 16:48:50 +01:00
"initialize": "",
"finalize": "",
"libs": [],
2021-12-03 02:34:14 +01:00
"x": 680,
"y": 480,
2020-12-01 21:12:29 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"52ea7d01.711034",
"40c12463.a1f84c",
"a4abb1ae.2ae418"
],
[
2021-12-03 02:34:14 +01:00
"20e0a8c8.edbeb"
2020-12-01 21:12:29 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "20e0a8c8.edbeb",
"type": "ui_toast",
"z": "baa1e3d9.cb29d",
"position": "dialog",
"displayTime": "3",
"highlight": "",
"sendall": true,
"outputs": 1,
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
2020-12-01 21:12:29 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"x": 950,
"y": 560,
"wires": [
[]
]
2020-12-01 21:12:29 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "c3e50240.82aa58",
"type": "mqtt out",
"z": "baa1e3d9.cb29d",
"name": "",
2021-12-03 02:34:14 +01:00
"topic": "",
"qos": "",
"retain": "",
"broker": "8dc3722c.06efa8",
2021-12-08 18:00:50 +01:00
"x": 1150,
2021-12-03 02:34:14 +01:00
"y": 440,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "3a4450b1.4459a8",
"type": "ui_button",
"z": "baa1e3d9.cb29d",
"name": "Stop Acquisition",
"group": "4322c187.e73e5",
"order": 11,
"width": 5,
"height": 1,
"passthru": true,
"label": "STOP ACQUISITION",
"tooltip": "",
"color": "",
"bgcolor": "#AD1625",
"icon": "cancel",
"payload": "{\"action\":\"stop\"}",
"payloadType": "json",
"topic": "imager/image",
"x": 460,
"y": 520,
"wires": [
[
"d74210ef.edc15"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d74210ef.edc15",
"type": "mqtt out",
"z": "baa1e3d9.cb29d",
"name": "",
"topic": "",
2021-12-03 02:34:14 +01:00
"qos": "",
"retain": "",
"broker": "8dc3722c.06efa8",
"x": 650,
"y": 520,
"wires": []
},
{
"id": "bb62da8a.ebc328",
"type": "ui_switch",
"z": "baa1e3d9.cb29d",
"name": "Pump direction",
"label": "Pump direction",
"tooltip": "BACKWARD / FORWARD",
"group": "4322c187.e73e5",
"order": 9,
"width": 5,
"height": 1,
"passthru": true,
"decouple": "false",
"topic": "pump_direction",
"style": "",
"onvalue": "FORWARD",
"onvalueType": "str",
"onicon": "",
"oncolor": "",
"offvalue": "BACKWARD",
"offvalueType": "str",
"officon": "",
"offcolor": "",
"x": 460,
"y": 560,
"wires": [
[
"6b2239f3.41fa3"
]
]
},
{
"id": "52ea7d01.711034",
"type": "function",
"z": "baa1e3d9.cb29d",
"name": "Encapsulate config",
"func": "msg.payload = {\n\t\"action\": \"update_config\",\n\t\"config\": {\n \t\"description\": {\n \t\t\"sample_project\": \"Project's name\",\n \t\t\"sample_id\": \"Sample ID\",\n \t\t\"sample_uuid\": \"Sample UUID (Autogenerated)\",\n \t\t\"sample_ship\": \"Ship's name\",\n \t\t\"sample_operator\": \"Operator's name\",\n \t\t\"sample_sampling_gear\": \"Sampling gear used\",\n \t\t\"sample_concentrated_sample_volume\": \"Volume of concentrated sample, in mL\",\n \t\t\"sample_total_volume\": \"Total volume filtered by the net used, in L\",\n \t\t\"sample_dilution_factor\": \"Dilution factor of the sample, 0.5 if diluted by 2, 2 if concentrated by 2\",\n \t\t\"sample_speed_through_water\": \"Speed of the boat through water when sampling, in kts\",\n \t\t\"acq_id\": \"Acquisition ID\",\n \t\t\"acq_uuid\": \"Acquisition UUID (Autogenerated)\",\n \t\t\"acq_instrument\": \"Instrument type\",\n \t\t\"acq_instrument_id\": \"Instrument ID\",\n \t\t\"acq_celltype\": \"Flow cell dimension thickness, in µm\",\n \t\t\"acq_minimum_mesh\": \"Minimum filtration mesh size, in µm\",\n \t\t\"acq_maximum_mesh\": \"Maximum filtration mesh size, in µm\",\n \t\t\"acq_min_esd\": \"\",\n \t\t\"acq_max_esd\": \"\",\n \t\t\"acq_volume\": \"Pumped volume, in mL\",\n\t\t \"acq_imaged_volume\": \"Total imaged volume, in mL\",\n \t\t\"acq_magnification\": \"Optical magnification\",\n \t\t\"acq_fnumber_objective\": \"Focal length of the objective, in mm\",\n \t\t\"acq_camera_name\": \"Name of the camera used\",\n \t\t\"acq_nb_frame\": \"Number of picture taken\",\n \"acq_local_datetime\": \"Instrument local datetime\",\n \"acq_camera_resolution\": \"Resolution of the images\",\n \"acq_camera_iso\": \"ISO Number of the images\",\n \"acq_camera_shutter_speed\": \"Shutter speed of the images, in µs\",\n \"acq_software\": \"Software version number\",\n \t\t\"object_date\": \"Sample collection date (or beginning if using a net)\",\n \t\t\"object_time\": \"Sample collection time (or beginning if using a net)\",\n \t\t\"object_lat\": \"Sample collection latitude (or beginning if using a net)\",\n \t\t\"object_lon\": \"Sample collection longitude (or beginning if using a net)\",\n \t\t\"object_depth_min\": \"Sample collection minimal depth, in m\",\n \t\t\"object_depth_max\": \"Sample collection maximum depth, in m\",\n \t\t\"process_pixel\": \"Pixel imaging resolution, in µm/pixel\",\n \t\t\"process_datetime\": \"Segmentation timestamp\",\n \t\t\"process_id\": \"Segmentation ID\",\n \t\t\"process_uuid\": \"Segmentation UUID (Autogenerated)\",\n \t\t\"process_source\": \"Code source link of the executed code\",\n \t\t\"process_commit\": \"Version reference of the executed code\",\n \t\t\"sample_gear_net_opening\": \"Sample mouth opening dimension, in mm\",\n \t\t\"object_date_end\": \"Sample end collection date when using a net\",\n \t\t\"object_time_end\": \"Sample end collection time when using a net\",\n \t\t\"object_lat_end\": \"Sample end collection latitude when using a net\",\n \t\t\"object_lon_end\": \"Sample end collection longitude when using a net\",\n \t},\n\t\t\"sample_project\": global.get(\"sample_project\"),\n\t\t\"sample_id\": global.get(\"sample_project\") + \"_\" + global.get(\"sample_id\"),\n\t\t\"sample_ship\": global.get(\"sample_ship\"),\n\t\t\"sample_operator\": global.get(\"sample_operator\"),\n\t\t\"sample_sampling_gear\": global.get(\"sample_sampling_gear\"),\n\t\t\"sample_concentrated_sample_volume\": global.get(\"sample_concentrated_sample_volume\"),\n\t\t\"sample_dilution_factor\": global.get(\"sample_dilution_factor\"),\n\t\t\"sample_speed_through_water\": global.get(\"sample_speed_through_water\"),\n\n\t\t\"acq_id\": global.get(\"sample_project\") + \"_\" + global.get(\"sample_id\")+ \"_\" + global.get(\"acq_id\"),\n\t\t\"acq_instrument\": global.get(\"acq_instrument\"),\n\t\t\"acq_instrument_id\": global.get(\"acq_instrument_id
"outputs": 2,
2021-12-03 02:34:14 +01:00
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 910,
"y": 440,
"wires": [
[
2021-12-03 02:34:14 +01:00
"29be525e.0c87fe"
],
[
"c3e50240.82aa58"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "40c12463.a1f84c",
"type": "delay",
"z": "baa1e3d9.cb29d",
"name": "",
"pauseType": "delay",
"timeout": "1",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"outputs": 1,
2021-12-01 13:30:23 +01:00
"x": 940,
2021-12-03 02:34:14 +01:00
"y": 480,
"wires": [
[
2021-12-03 02:34:14 +01:00
"c3e50240.82aa58"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4d1b02cb.83b51c",
"type": "ui_button",
"z": "baa1e3d9.cb29d",
"name": "",
"group": "4322c187.e73e5",
"order": 10,
"width": 5,
"height": 1,
"passthru": false,
"label": "Update config",
"tooltip": "",
"color": "",
"bgcolor": "",
"className": "",
2021-12-03 02:34:14 +01:00
"icon": "save",
"payload": "",
"payloadType": "str",
"topic": "imager/image",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"x": 460,
"y": 440,
"wires": [
[
2021-12-03 02:34:14 +01:00
"52ea7d01.711034"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "5921d0d0.a3d568",
"type": "subflow:1c24ad9c.bebec2",
"z": "baa1e3d9.cb29d",
"name": "",
2021-12-03 02:34:14 +01:00
"env": [],
"x": 110,
"y": 220,
"wires": [
[
"f3658d30.b8448",
"de2c90cf.b73b08",
"4be09c97f86897d9",
"f573206abefa9518",
"f948151ab4031df4",
"5e3dec55.881074",
"d3ca8847.4d1ae"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "f3658d30.b8448",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "baa1e3d9.cb29d",
"name": "get acq_id",
"func": "msg.payload = msg.payload.acq_id;\nreturn msg;",
"outputs": 1,
"noerr": 0,
2020-12-08 16:48:50 +01:00
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 330,
"y": 240,
"wires": [
[
2021-12-03 02:34:14 +01:00
"6b34c456.83178c"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "de2c90cf.b73b08",
"type": "function",
"z": "baa1e3d9.cb29d",
"name": "get acq_celltype",
"func": "msg.payload = msg.payload.acq_celltype;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 340,
"y": 80,
"wires": [
[
2021-12-03 02:34:14 +01:00
"cc0ca68b.4263a8"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "5e3dec55.881074",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "baa1e3d9.cb29d",
"name": "get acq_minimum_mesh",
"func": "msg.payload = msg.payload.acq_minimum_mesh;\nreturn msg;",
"outputs": 1,
"noerr": 0,
2021-12-03 02:34:14 +01:00
"x": 370,
"y": 160,
"wires": [
[
2021-12-03 02:34:14 +01:00
"f59a2f0d.5e9af"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d3ca8847.4d1ae",
"type": "function",
"z": "baa1e3d9.cb29d",
"name": "get acq_maximum_mesh",
"func": "msg.payload = msg.payload.acq_maximum_mesh;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 370,
"y": 200,
"wires": [
2021-12-03 02:34:14 +01:00
[
"6008a8bb.259f08"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "b402f719.55bc98",
"type": "inject",
"z": "baa1e3d9.cb29d",
"name": "Default: FORWARD",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "pump_direction",
"payload": "FORWARD",
"payloadType": "str",
"x": 220,
"y": 560,
"wires": [
[
2021-12-03 02:34:14 +01:00
"bb62da8a.ebc328"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6b2239f3.41fa3",
"type": "function",
"z": "baa1e3d9.cb29d",
"name": "set global",
"func": "global.set(msg.topic, msg.payload);",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 660,
"y": 560,
"wires": [
[]
]
},
{
"id": "51b4d0df.d70a88",
"type": "ui_text_input",
2021-12-03 02:34:14 +01:00
"z": "baa1e3d9.cb29d",
"name": "nb_frame",
"label": "Number of images to acquire",
"tooltip": "",
"group": "4322c187.e73e5",
"order": 2,
"width": 5,
"height": 1,
"passthru": true,
"mode": "number",
"delay": 300,
"topic": "nb_frame",
"sendOnBlur": true,
"className": "",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"x": 680,
"y": 280,
"wires": [
[
2021-12-03 02:34:14 +01:00
"fb887036.12429",
"67091ac0.8f9f6c",
"99b11fe4.2795d"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "999065ca.27edb8",
"type": "switch",
"z": "baa1e3d9.cb29d",
"name": "topic filter",
"property": "topic",
"propertyType": "msg",
"rules": [
{
2021-12-03 02:34:14 +01:00
"t": "eq",
"v": "status/pump",
"vt": "str"
},
{
"t": "eq",
"v": "status/focus",
"vt": "str"
},
{
"t": "eq",
"v": "status/imager",
"vt": "str"
}
],
2021-12-03 02:34:14 +01:00
"checkall": "true",
"repair": false,
"outputs": 3,
"x": 320,
"y": 800,
"wires": [
2021-12-03 02:34:14 +01:00
[
"a46a1e7f.88a92",
"6742014e.1bb238"
],
[
"c516d9ea.f7f6e",
"6742014e.1bb238"
],
[
"307c851e.fb0f7a",
"2b9d6988.d84836"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "2b009bd7.c07004",
"type": "ui_text",
"z": "baa1e3d9.cb29d",
"group": "70de8209.68416c",
"order": 3,
"width": 10,
"height": 2,
"name": "imager",
"label": "Imager status:",
"format": "{{msg.payload.status}}",
"layout": "col-center",
"x": 770,
"y": 740,
"wires": []
},
{
"id": "c516d9ea.f7f6e",
"type": "ui_text",
"z": "baa1e3d9.cb29d",
"group": "70de8209.68416c",
"order": 1,
"width": 5,
"height": 1,
2021-12-03 02:34:14 +01:00
"name": "focus",
"label": "Focus status:",
"format": "{{msg.payload.status}}",
"layout": "col-center",
"x": 770,
"y": 700,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "a46a1e7f.88a92",
"type": "ui_text",
"z": "baa1e3d9.cb29d",
"group": "70de8209.68416c",
"order": 2,
"width": 5,
"height": 1,
"name": "pump",
"label": "Pump status:",
"format": "{{msg.payload.status}}",
"layout": "col-center",
"x": 770,
"y": 660,
"wires": []
},
{
"id": "bb628f8d.98f108",
"type": "link in",
"z": "baa1e3d9.cb29d",
"name": "Status for fluidic module",
"links": [
"58f2e0f.4e8b12"
],
"x": 175,
"y": 800,
"wires": [
2021-12-03 02:34:14 +01:00
[
"999065ca.27edb8"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "307c851e.fb0f7a",
"type": "switch",
"z": "baa1e3d9.cb29d",
"name": "Imaging state",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "jsonata_exp",
"v": "$contains(msg.payload.status, \"jpg\")\t",
"vt": "jsonata"
},
{
"t": "else"
}
],
"checkall": "false",
"repair": false,
"outputs": 2,
"x": 540,
"y": 880,
"wires": [
[
2021-12-03 02:34:14 +01:00
"db8e3dde.44efb8"
],
[
"6742014e.1bb238"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "db8e3dde.44efb8",
"type": "function",
"z": "baa1e3d9.cb29d",
"name": "img_counter",
"func": "img_counter=flow.get('img_counter')\nif (img_counter === undefined || img_counter === \"\"){\n img_counter = 0\n}\nimg_counter=img_counter+1\nflow.set('img_counter',img_counter)\nmsg.payload = img_counter\nmsg.payload = (100 * img_counter/global.get('nb_frame')).toFixed(2)\nreturn msg;",
"outputs": 1,
2021-12-03 02:34:14 +01:00
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 770,
"y": 860,
"wires": [
[
2021-12-03 02:34:14 +01:00
"1a2e721a.f5e876"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "59164d65.e7993c",
"type": "ui_toast",
"z": "baa1e3d9.cb29d",
"position": "top right",
"displayTime": "5",
"highlight": "",
"sendall": true,
"outputs": 0,
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
"name": "",
2021-12-03 02:51:25 +01:00
"x": 1260,
2021-12-03 02:34:14 +01:00
"y": 900,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "8c7348aa.1962e8",
"type": "template",
"z": "baa1e3d9.cb29d",
"name": "Create sentence",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "The {{topic}} is {{payload.status}}",
"output": "str",
"x": 1050,
"y": 900,
"wires": [
[
2021-12-03 02:34:14 +01:00
"59164d65.e7993c"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6742014e.1bb238",
"type": "change",
"z": "baa1e3d9.cb29d",
"name": "Remove high-level topic",
"rules": [
{
"t": "change",
"p": "topic",
"pt": "msg",
"from": "status/",
"fromt": "str",
"to": "",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 810,
"y": 900,
"wires": [
[
2021-12-03 02:34:14 +01:00
"8c7348aa.1962e8"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "1a2e721a.f5e876",
"type": "ui_gauge",
"z": "baa1e3d9.cb29d",
"name": "progress donut",
"group": "b5d61bc7.54fe48",
"order": 3,
"width": 6,
"height": 3,
"gtype": "donut",
"title": "",
"label": "%",
"format": "{{value}}",
"min": 0,
"max": "100",
"colors": [
"#ffa83f",
"#e6ff02",
"#00dfe9"
],
"seg1": "50",
"seg2": "75",
"x": 1260,
"y": 860,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "9bd72495.a8a098",
"type": "ui_text",
"z": "baa1e3d9.cb29d",
"group": "4322c187.e73e5",
"order": 5,
"width": 5,
"height": 1,
"name": "Total imaged volume",
"label": "Total imaged volume",
"format": "{{msg.payload}}",
"layout": "col-center",
"x": 1200,
"y": 140,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "99b11fe4.2795d",
"type": "function",
"z": "baa1e3d9.cb29d",
"name": "imaged volume calc",
"func": "camera = global.get(\"acq_camera\");\ncell = global.get(\"acq_celltype\");\nnb_frame = global.get(\"nb_frame\");\nprocess_pixel = global.get(\"process_pixel\");\n\nvar volume = 0\nif (camera == \"HQ Camera\"){\n volume = nb_frame * (process_pixel*4056*process_pixel*3040*cell/1000000000) / 1000;\n}\nelse if (camera == \"Camera v2.1\"){\n volume = nb_frame * (process_pixel*3280*process_pixel*2464*cell/1000000000) / 1000;\n}\nelse{\n msg.payload = \"The camera is not known to this system\";\n return msg;\n}\n\nglobal.set(\"acq_imaged_volume\", volume.toFixed(4))\n\nmsg.payload = volume.toFixed(2) + \" mL\"\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 960,
"y": 140,
"wires": [
2021-12-03 02:34:14 +01:00
[
"9bd72495.a8a098"
]
],
"info": "### Focusing\n##### focus.py `nb_step` `orientation`\n\n- `nb_step` : **integer** (from 1 to 100000) - number of step to perform by the stage (about 31um/step)\n- `orientation` : **string** - orientation of the focus either `up` or `down`\n\nExample:\n\n python3.7 $HOME/PlanktoScope/scripts/focus.py 650 up\n"
},
{
2021-12-03 02:34:14 +01:00
"id": "8e16aa2f.e40398",
"type": "ui_text",
"z": "baa1e3d9.cb29d",
"group": "4322c187.e73e5",
"order": 7,
"width": 5,
"height": 1,
"name": "Total pumped volume",
"label": "Total pumped volume",
"format": "{{msg.payload}} mL",
"layout": "col-center",
"x": 1200,
"y": 300,
"wires": []
},
{
"id": "67091ac0.8f9f6c",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "baa1e3d9.cb29d",
"name": "acq_volume calc",
"func": "var imaging_pump_volume = global.get(\"imaging_pump_volume\");\nvar nb_frame = global.get(\"nb_frame\");\nvar acq_volume = 0\n\nacq_volume = (Number(nb_frame)*Number(imaging_pump_volume)).toFixed(2)\nglobal.set(\"acq_volume\", acq_volume)\n\nmsg.payload = acq_volume\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 950,
"y": 300,
"wires": [
[
2021-12-03 02:34:14 +01:00
"8e16aa2f.e40398"
]
2021-12-03 02:34:14 +01:00
],
"info": "### Focusing\n##### focus.py `nb_step` `orientation`\n\n- `nb_step` : **integer** (from 1 to 100000) - number of step to perform by the stage (about 31um/step)\n- `orientation` : **string** - orientation of the focus either `up` or `down`\n\nExample:\n\n python3.7 $HOME/PlanktoScope/scripts/focus.py 650 up\n"
},
{
2021-12-03 02:34:14 +01:00
"id": "2b9d6988.d84836",
"type": "switch",
"z": "baa1e3d9.cb29d",
"name": "",
2021-12-03 02:34:14 +01:00
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "hask",
"v": "status",
"vt": "str"
},
{
"t": "hask",
"v": "camera_name",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 2,
"x": 650,
"y": 760,
"wires": [
[
2021-12-03 02:34:14 +01:00
"2b009bd7.c07004"
],
[
2021-12-03 02:34:14 +01:00
"3bbb756a.84190a"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "3bbb756a.84190a",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "baa1e3d9.cb29d",
"name": "set camera name",
"func": "global.set(\"acq_camera\", msg.payload.camera_name);\n\nmsg.payload = msg.payload.camera_name;\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 810,
"y": 780,
"wires": [
[
2021-12-03 02:34:14 +01:00
"559a8085.1d6b9"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "559a8085.1d6b9",
"type": "link out",
"z": "baa1e3d9.cb29d",
"name": "Camera Name",
"links": [
"30067f35.532f2",
"572a6daa.6004c4",
"cfc783d7.d6ceb"
],
"x": 965,
"y": 780,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "49ea1123.ee1768",
"type": "ui_numeric",
"z": "baa1e3d9.cb29d",
"name": "sleep_before",
"label": "Delay to stabilize image (s)",
"tooltip": "Happens before every capture",
"group": "4322c187.e73e5",
"order": 4,
"width": 5,
"height": 1,
"wrap": false,
"passthru": true,
"topic": "sleep_before",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"format": "{{value}}",
"min": "0.1",
"max": "5",
"step": "0.1",
"className": "",
2021-12-03 02:34:14 +01:00
"x": 670,
"y": 120,
"wires": [
[
2021-12-03 02:34:14 +01:00
"fb887036.12429"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "572a6daa.6004c4",
"type": "link in",
"z": "baa1e3d9.cb29d",
"name": "",
2021-12-03 02:34:14 +01:00
"links": [
"559a8085.1d6b9"
],
"x": 795,
"y": 140,
"wires": [
[
2021-12-03 02:34:14 +01:00
"99b11fe4.2795d"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "8032ea50.1b57f",
"type": "ui_template",
"z": "baa1e3d9.cb29d",
"group": "b5d61bc7.54fe48",
"name": "progress header",
"order": 1,
"width": 0,
"height": 0,
"format": "<center><h2>Capture progress</h2></center>",
"storeOutMessages": false,
"fwdInMessages": false,
"resendOnRefresh": false,
"templateScope": "local",
2021-12-03 02:51:25 +01:00
"x": 1260,
2021-12-03 02:34:14 +01:00
"y": 820,
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "df1ea904.cd261",
"type": "ui_slider",
"z": "baa1e3d9.cb29d",
"name": "imaging_pump_volume",
"label": "Pumped volume (mL)",
"tooltip": "inbetween frames",
"group": "4322c187.e73e5",
"order": 3,
"width": 0,
"height": 0,
"passthru": true,
"outs": "end",
"topic": "imaging_pump_volume",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"min": "0.001",
"max": "0.5",
"step": "0.001",
"className": "",
2021-12-03 02:34:14 +01:00
"x": 630,
"y": 320,
"wires": [
[
2021-12-03 02:34:14 +01:00
"fb887036.12429",
"67091ac0.8f9f6c"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "a4abb1ae.2ae418",
"type": "subflow:1c24ad9c.bebec2",
"z": "baa1e3d9.cb29d",
"name": "",
"env": [],
"x": 930,
"y": 520,
"wires": [
[]
]
},
{
"id": "4be09c97f86897d9",
"type": "function",
"z": "baa1e3d9.cb29d",
"name": "get nb_frame",
"func": "msg.payload = msg.payload.nb_frame;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
2021-12-03 02:51:25 +01:00
"x": 330,
2021-12-03 02:34:14 +01:00
"y": 280,
"wires": [
2021-12-03 02:34:14 +01:00
[
"51b4d0df.d70a88"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "f573206abefa9518",
"type": "function",
"z": "baa1e3d9.cb29d",
"name": "get imaging_pump_volume",
"func": "msg.payload = msg.payload.imaging_pump_volume;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 380,
"y": 320,
"wires": [
[
2021-12-03 02:34:14 +01:00
"df1ea904.cd261"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "f948151ab4031df4",
"type": "function",
"z": "baa1e3d9.cb29d",
"name": "get sleep_before",
"func": "msg.payload = msg.payload.sleep_before;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
2021-12-03 02:51:25 +01:00
"x": 340,
2021-12-03 02:34:14 +01:00
"y": 120,
"wires": [
[
2021-12-03 02:34:14 +01:00
"49ea1123.ee1768"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "c515edcd8681c244",
"type": "link in",
"z": "baa1e3d9.cb29d",
"name": "Cam Stream",
"links": [
"49111a72f4e32740"
],
"x": 275,
"y": 20,
"wires": [
2021-12-03 02:34:14 +01:00
[
"a77625dc21c8a3cc"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "a77625dc21c8a3cc",
"type": "template",
2021-12-03 02:34:14 +01:00
"z": "baa1e3d9.cb29d",
"name": "Prepare stream HTML",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
2021-12-03 02:34:14 +01:00
"template": "<center>\n <img src=\"http://{{payload}}:8000/stream.mjpg\"\n onerror=\"this.onerror=null; this.src='http://{{payload}}:80/error.svg'\"\n alt=\"If you see this, there probably is an error either with your camera or with the python service. Please restart your machine.\"\n width=\"100%\" height=\"100%\">\n</center>",
"output": "str",
2021-12-03 02:34:14 +01:00
"x": 420,
"y": 20,
"wires": [
[
2021-12-03 02:34:14 +01:00
"bb2bb7ce.1d1458"
]
]
},
{
"id": "ad153212312f0347",
"type": "ui_ui_control",
"z": "baa1e3d9.cb29d",
"name": "onTab",
"events": "all",
2021-12-08 18:00:50 +01:00
"x": 330,
"y": 380,
"wires": [
[
"9b69ea945b587192"
]
]
},
{
"id": "9b69ea945b587192",
"type": "function",
"z": "baa1e3d9.cb29d",
"name": "check tab",
"func": "if (msg.name == \"Fluidic Acquisition\"){\n return { \"topic\": \"imager/ image\" }\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
2021-12-08 18:00:50 +01:00
"x": 480,
"y": 380,
"wires": [
[
"52ea7d01.711034"
]
],
"outputLabels": [
"interface"
]
},
{
2021-12-03 02:34:14 +01:00
"id": "9d6abe67.6bb3d",
"type": "ui_button",
2021-12-03 02:34:14 +01:00
"z": "cb95299c.2817c8",
"name": "",
"group": "64903b47.4034e4",
"order": 1,
"width": 0,
"height": 0,
"passthru": false,
2021-12-03 02:34:14 +01:00
"label": "Home",
"tooltip": "",
"color": "",
"bgcolor": "",
2021-12-03 02:34:14 +01:00
"icon": "home",
"payload": "{\"tab\":\"Home\"}",
"payloadType": "json",
"topic": "",
"x": 430,
"y": 600,
"wires": [
[
"f0fb77cf.8f1c28"
]
]
},
{
"id": "f0fb77cf.8f1c28",
"type": "ui_ui_control",
"z": "cb95299c.2817c8",
"name": "",
"events": "change",
"x": 740,
"y": 600,
"wires": [
2021-09-06 18:59:00 +02:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "8ec68b82.17e3d8",
"type": "ui_button",
"z": "cb95299c.2817c8",
"name": "Start segmentation",
"group": "abeb6dad.635a2",
"order": 10,
"width": 5,
"height": 1,
"passthru": false,
"label": "Start segmentation",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "",
"payload": "{\"action\":\"segment\"}",
"payloadType": "json",
"topic": "segmenter/segment",
"x": 370,
2021-05-06 03:11:45 +02:00
"y": 460,
"wires": [
[
2021-12-03 02:34:14 +01:00
"33c28dc1.238002",
"c72a1064.1ec388"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "27be7971.b3fbce",
"type": "ui_button",
"z": "cb95299c.2817c8",
"d": true,
"name": "Stop segmentation",
"group": "abeb6dad.635a2",
"order": 12,
"width": 4,
"height": 1,
"passthru": true,
"label": "Stop segmentation",
"tooltip": "",
"color": "",
"bgcolor": "#AD1625",
"icon": "",
"payload": "{\"action\":\"stop\"}",
"payloadType": "json",
"topic": "segmenter/segment",
"x": 370,
"y": 520,
"wires": [
2021-12-03 02:34:14 +01:00
[
"16f3cef4.0acac9"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "16f3cef4.0acac9",
"type": "mqtt out",
"z": "cb95299c.2817c8",
"name": "",
"topic": "",
2021-12-03 02:34:14 +01:00
"qos": "",
"retain": "",
"broker": "8dc3722c.06efa8",
"x": 850,
"y": 520,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "8f3788f6.ddcf98",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "cb95299c.2817c8",
"name": "obj_counter",
"func": "obj_counter = flow.get('obj_counter')\nobj_counter = obj_counter + 1\nflow.set('obj_counter', obj_counter)\nmsg.payload = obj_counter\nmsg.topic = \"object count\"\nreturn msg",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 810,
"y": 800,
"wires": [
[
2021-12-03 02:34:14 +01:00
"fa3b7929.ac7da8",
"9d53dbe2.dbffe8"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "aa38dbbc.cf0a9",
"type": "switch",
"z": "cb95299c.2817c8",
"name": "Segmenter",
"property": "topic",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "status/segmenter",
"vt": "str"
},
{
"t": "eq",
"v": "status/segmenter/name",
"vt": "str"
},
{
"t": "eq",
"v": "status/segmenter/object_id",
"vt": "str"
},
{
"t": "eq",
"v": "status/segmenter/metric",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 4,
"x": 490,
"y": 740,
"wires": [
[
2021-12-03 02:34:14 +01:00
"b9a23b91.93638",
"6919465f.332e5"
],
2021-12-03 02:34:14 +01:00
[
"49af3d24.1799e4"
],
[
"8f3788f6.ddcf98"
],
[
"50ce901f.b5034"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "9d53dbe2.dbffe8",
"type": "ui_chart",
"z": "cb95299c.2817c8",
"name": "counter graph",
"group": "46be9c86.dea684",
"order": 7,
"width": 10,
"height": 2,
"label": "",
"chartType": "horizontalBar",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "Objects count will be shown here once the segmentation is started",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": true,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"x": 1340,
"y": 840,
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "50ce901f.b5034",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "cb95299c.2817c8",
"name": "ex : area",
"func": "// Payload looks like this:\n// {'name': '01_13_28_232066_0',\n// 'metadata': {\n// 'label': 0, 'width': 29, 'height': 80, ....\n\nmsg.payload=msg.payload.metadata.area_exc\nmsg.topic=\"area\"\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 800,
"y": 920,
"wires": [
[
2021-12-03 02:34:14 +01:00
"458cd82e.03d258"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "a4f0f0d1.3aca88",
"type": "ui_toast",
2021-12-03 02:34:14 +01:00
"z": "cb95299c.2817c8",
"position": "top right",
"displayTime": "5",
"highlight": "",
"sendall": true,
2021-12-03 02:34:14 +01:00
"outputs": 0,
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
"name": "",
2021-12-03 02:51:25 +01:00
"x": 1340,
2021-12-03 02:34:14 +01:00
"y": 680,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "458cd82e.03d258",
"type": "ui_chart",
"z": "cb95299c.2817c8",
"name": "Area chart",
"group": "46be9c86.dea684",
"order": 9,
"width": 10,
"height": 6,
"label": "",
2021-12-03 02:34:14 +01:00
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "Objects area will be shown here once the segmentation is started",
"dot": true,
"ymin": "",
"ymax": "",
"removeOlder": 1,
"removeOlderPoints": "1000",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": true,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
2021-12-03 02:34:14 +01:00
"outputs": 1,
"x": 1330,
"y": 920,
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "49af3d24.1799e4",
"type": "debug",
"z": "cb95299c.2817c8",
"name": "segmentation name",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
2021-12-03 02:51:25 +01:00
"x": 830,
2021-12-03 02:34:14 +01:00
"y": 760,
"wires": []
},
{
"id": "7088a5be.0c79a4",
"type": "template",
"z": "cb95299c.2817c8",
"name": "Create sentence",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "The {{topic}} is {{payload.status}}",
"output": "str",
2021-12-03 02:51:25 +01:00
"x": 1100,
2021-12-03 02:34:14 +01:00
"y": 680,
"wires": [
[
2021-12-03 02:34:14 +01:00
"a4f0f0d1.3aca88"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "b9a23b91.93638",
"type": "change",
2021-12-03 02:34:14 +01:00
"z": "cb95299c.2817c8",
"name": "Remove high-level topic",
"rules": [
{
2021-12-03 02:34:14 +01:00
"t": "change",
"p": "topic",
"pt": "msg",
2021-12-03 02:34:14 +01:00
"from": "status/",
"fromt": "str",
"to": "",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
2021-12-03 02:34:14 +01:00
"x": 850,
"y": 680,
"wires": [
[
2021-12-03 02:34:14 +01:00
"7088a5be.0c79a4"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6919465f.332e5",
"type": "ui_text",
"z": "cb95299c.2817c8",
"group": "46be9c86.dea684",
"order": 1,
"width": 10,
"height": 1,
"name": "segmenter",
"label": "Segmenter status:",
"format": "{{msg.payload.status}}",
"layout": "row-spread",
"x": 810,
"y": 720,
"wires": []
},
{
"id": "fa3b7929.ac7da8",
"type": "ui_text",
"z": "cb95299c.2817c8",
"group": "46be9c86.dea684",
"order": 5,
"width": 4,
"height": 1,
"name": "counter",
"label": "",
"format": "{{msg.payload}}",
"layout": "col-center",
"x": 1320,
"y": 800,
"wires": []
},
{
"id": "640ece83.88cab",
"type": "inject",
"z": "cb95299c.2817c8",
"name": "Init graphs",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "object count",
"payload": "0",
"payloadType": "num",
"x": 810,
"y": 880,
"wires": [
[
2021-12-03 02:34:14 +01:00
"ac439738.28bc18",
"4f3f7c4a.cb21c4"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "25867454.a8e334",
"type": "link in",
"z": "cb95299c.2817c8",
"name": "Segmenter module status",
"links": [
"dcf5bd45.16a8d"
],
"x": 295,
"y": 740,
"wires": [
[
"aa38dbbc.cf0a9"
]
]
},
{
"id": "c8749cbb.55254",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "cb95299c.2817c8",
"name": "set global",
"func": "global.set(msg.topic, msg.payload);",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"libs": [],
"x": 960,
"y": 380,
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d43c3ed9.e6cb1",
"type": "ui_template",
"z": "cb95299c.2817c8",
"group": "46be9c86.dea684",
"name": "Object counts",
"order": 3,
"width": 0,
"height": 0,
"format": "<div><center><h2>Object counts</h2></center></div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"x": 1340,
"y": 760,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "857e90c1.cd25b",
"type": "ui_template",
"z": "cb95299c.2817c8",
"group": "46be9c86.dea684",
"name": "Area chart",
"order": 8,
"width": 0,
"height": 0,
"format": "<div><center><h2>Area chart</h2></center></div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"x": 1330,
"y": 880,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "33c28dc1.238002",
"type": "function",
"z": "cb95299c.2817c8",
"name": "prepare segmentation",
"func": "global.set('obj_counter', 0);\n\nvar segmentation_list = flow.get('segmentation_list')\nif (segmentation_list !== undefined && segmentation_list !== \"\") {\n msg.payload['path'] = segmentation_list\n}\n\n\nvar force = flow.get('force')\nif (force !== undefined && force !== \"\") {\n msg.payload['settings'] = {\"force\":Boolean(force)}\n}\n\n\nvar recursive = flow.get('recursive')\nif (recursive !== undefined && recursive !== \"\") {\n if (\"settings\" in msg.payload){\n msg.payload.settings[\"recursive\"] = Boolean(recursive)\n }\n else{\n msg.payload['settings'] = {\"recursive\": Boolean(recursive)}\n }\n}\n\nvar ecotaxa = flow.get('ecotaxa')\nif (ecotaxa !== undefined && ecotaxa !== \"\") {\n if (\"settings\" in msg.payload){\n msg.payload.settings[\"ecotaxa\"] = Boolean(ecotaxa)\n }\n else{\n msg.payload['settings'] = {\"ecotaxa\": Boolean(ecotaxa)}\n }\n}\n\nvar keep = flow.get('keep')\nif (keep !== undefined && keep !== \"\") {\n if (\"settings\" in msg.payload){\n msg.payload.settings[\"keep\"] = Boolean(keep)\n }\n else{\n msg.payload['settings'] = {\"keep\": Boolean(keep)}\n }\n}\n\nvar process_id = global.get('process_id')\nif (process_id !== undefined && process_id !== \"\") {\n if (\"settings\" in msg.payload){\n msg.payload.settings[\"process_id\"] = process_id\n }\n else{\n msg.payload['settings'] = {\"process_id\": process_id}\n }\n}\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 620,
"y": 460,
"wires": [
[
2021-12-03 02:34:14 +01:00
"16f3cef4.0acac9"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "fa12f9f8.00cfa8",
"type": "subflow:1c24ad9c.bebec2",
"z": "cb95299c.2817c8",
"name": "",
2021-12-03 02:34:14 +01:00
"env": [],
"x": 430,
"y": 380,
"wires": [
2021-12-03 02:34:14 +01:00
[
"3b72d11c.86e9e6"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "3b72d11c.86e9e6",
"type": "function",
"z": "cb95299c.2817c8",
"name": "get process_id",
"func": "msg.payload = msg.payload.process_id;\nmsg.topic = \"process_id\";\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 600,
"y": 380,
"wires": [
[
"56971109c8be3b54"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "3ea12061.ce62c",
"type": "ui_list",
"z": "cb95299c.2817c8",
"group": "abeb6dad.635a2",
"name": "",
"order": 9,
"width": 10,
"height": 11,
"lineType": "one",
"actionType": "check",
"allowHTML": false,
"outputs": 1,
"topic": "",
2021-12-03 02:34:14 +01:00
"x": 790,
"y": 140,
"wires": [
2021-12-03 02:34:14 +01:00
[
"cb3b87b5.63c4"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "8bd8fb2c.53fa4",
"type": "dir2files",
"z": "cb95299c.2817c8",
"name": "",
"dirname": "/home/pi/data/img/",
"pathRegex": "",
"isRecursive": true,
"findDir": true,
"isArray": true,
"x": 460,
"y": 140,
"wires": [
[
2021-12-03 02:34:14 +01:00
"ba2947.c854deb8"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "127d4ee.f8ad1b1",
"type": "ui_button",
2021-12-03 02:34:14 +01:00
"z": "cb95299c.2817c8",
"name": "Refresh",
"group": "abeb6dad.635a2",
"order": 8,
"width": 0,
"height": 0,
"passthru": false,
2021-12-03 02:34:14 +01:00
"label": "Update acquisition's folder list",
"tooltip": "Refresh the list of previous acquisitions",
"color": "",
2021-12-03 02:34:14 +01:00
"bgcolor": "",
"icon": "mi-find_replace",
"payload": "",
"payloadType": "date",
"topic": "update",
"x": 260,
"y": 140,
"wires": [
[
2021-12-03 02:34:14 +01:00
"8bd8fb2c.53fa4",
"56f845f5.e7c054"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "946ce9ee.092cf",
"type": "inject",
"z": "cb95299c.2817c8",
"name": "Init",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 270,
"y": 100,
"wires": [
[
2021-12-03 02:34:14 +01:00
"8bd8fb2c.53fa4",
"56f845f5.e7c054"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "cb3b87b5.63c4",
"type": "function",
"z": "cb95299c.2817c8",
"name": "update segmentation_list",
"func": "var segmentation_list = flow.get('segmentation_list');\n\nif (segmentation_list === undefined || segmentation_list === \"\") {\n segmentation_list = []\n console.log(\"error\")\n}\n\npath = \"/home/pi/data/img/\" + msg.payload.title\n\nif (msg.payload.isChecked){\n if (segmentation_list.includes(path) === false){\n segmentation_list.push(path)\n }\n // Element already in list, don't push it more than once\n //segmentation_list.push(msg.payload[\"title\"])\n}\nelse {\n var pos = segmentation_list.indexOf(path)\n segmentation_list.splice(pos, 1)\n}\n\nflow.set('segmentation_list', segmentation_list)",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:51:25 +01:00
"x": 990,
2021-12-03 02:34:14 +01:00
"y": 140,
"wires": [
[]
]
},
{
"id": "56f845f5.e7c054",
"type": "change",
"z": "cb95299c.2817c8",
"name": "Reset segmentation_list",
"rules": [
{
2021-12-03 02:34:14 +01:00
"t": "set",
"p": "segmentation_list",
"pt": "flow",
"to": "[]",
"tot": "json"
}
],
2021-12-03 02:34:14 +01:00
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 510,
2021-12-03 02:34:14 +01:00
"y": 100,
"wires": [
[]
]
},
{
"id": "7fc72364.8f038c",
"type": "ui_template",
"z": "cb95299c.2817c8",
"group": "abeb6dad.635a2",
"name": "Update message",
"order": 1,
"width": 10,
"height": 3,
"format": "<div><left>You can choose here in which folder(s) you want the segmentation script to run. A few details though:\n<br>The segmentation is run recursively in all folders. So if you select a top level folder, the segmentation will be run in all subfolders.\n<br>Also, you will be able to chose wether for force the segmentation for folders in which it has run already.</left></div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"x": 270,
"y": 40,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "ba2947.c854deb8",
"type": "function",
"z": "cb95299c.2817c8",
"name": "remove common",
"func": "function remove_path(item, index, array) {\n array[index] = item.replace(\"/home/pi/data/img/\", \"\")\n} \n\nmsg.payload.forEach(remove_path)\n\nreturn msg",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 630,
"y": 140,
"wires": [
2020-12-09 15:27:12 +01:00
[
2021-12-03 02:34:14 +01:00
"3ea12061.ce62c"
]
]
2020-12-03 16:45:01 +01:00
},
2020-12-03 23:13:08 +01:00
{
2021-12-03 02:34:14 +01:00
"id": "21af0db1.c2c182",
"type": "ui_multistate_switch",
"z": "cb95299c.2817c8",
"name": "recursive toggle",
"group": "abeb6dad.635a2",
"order": 4,
"width": 5,
"height": 1,
"label": "Recursive folder",
"stateField": "payload",
"enableField": "enable",
"rounded": true,
"useThemeColors": true,
"hideSelectedLabel": false,
2020-12-03 23:13:08 +01:00
"options": [
{
2021-12-03 02:34:14 +01:00
"label": "No",
"value": "0",
"valueType": "num",
"color": "#009933"
2020-12-03 23:13:08 +01:00
},
{
2021-12-03 02:34:14 +01:00
"label": "Yes",
"value": "1",
"valueType": "num",
"color": "#999999"
2020-12-03 23:13:08 +01:00
}
],
2021-12-03 02:34:14 +01:00
"x": 480,
"y": 220,
2020-12-03 23:13:08 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"880b192a.88e2d"
2020-12-03 23:13:08 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "dffb9881.feef8",
"type": "ui_multistate_switch",
"z": "cb95299c.2817c8",
"name": "force toggle",
"group": "abeb6dad.635a2",
"order": 3,
"width": 5,
"height": 1,
"label": "Force rework",
"stateField": "payload",
"enableField": "enable",
"rounded": true,
"useThemeColors": true,
"hideSelectedLabel": false,
2020-12-03 23:13:08 +01:00
"options": [
{
2021-12-03 02:34:14 +01:00
"label": "No",
"value": "0",
"valueType": "num",
"color": "#009933"
2020-12-03 23:13:08 +01:00
},
{
2021-12-03 02:34:14 +01:00
"label": "Yes",
"value": "1",
"valueType": "num",
"color": "#999999"
2020-12-03 23:13:08 +01:00
}
],
2021-12-03 02:34:14 +01:00
"x": 470,
"y": 300,
2020-12-03 23:13:08 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"a4f68fa6.5d77f8"
2020-12-03 23:13:08 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "880b192a.88e2d",
"type": "change",
"z": "cb95299c.2817c8",
"name": "",
"rules": [
2020-12-03 23:13:08 +01:00
{
2021-12-03 02:34:14 +01:00
"t": "set",
"p": "recursive",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 690,
"y": 240,
"wires": [
[]
]
},
{
"id": "a4f68fa6.5d77f8",
"type": "change",
"z": "cb95299c.2817c8",
"name": "",
"rules": [
2020-12-03 23:13:08 +01:00
{
2021-12-03 02:34:14 +01:00
"t": "set",
"p": "force",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 670,
"y": 280,
"wires": [
[]
]
},
{
"id": "1ef1b43b.b0f064",
"type": "inject",
"z": "cb95299c.2817c8",
"name": "0",
"props": [
2020-12-03 23:13:08 +01:00
{
2021-12-03 02:34:14 +01:00
"p": "payload"
2020-12-03 23:13:08 +01:00
}
],
2021-12-03 02:34:14 +01:00
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "0",
"payloadType": "num",
"x": 270,
"y": 280,
2020-12-03 23:13:08 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"dffb9881.feef8",
"a4f68fa6.5d77f8"
2020-12-03 23:13:08 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "f078c068.eacf58",
"type": "ui_template",
"z": "cb95299c.2817c8",
"group": "46be9c86.dea684",
"name": "Stream Segmented object",
"order": 2,
"width": 10,
"height": 8,
"format": "<span ng-bind-html=\"msg.payload\"></span>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": false,
"templateScope": "local",
2021-12-03 02:51:25 +01:00
"x": 1370,
2021-12-03 02:34:14 +01:00
"y": 600,
"wires": [
[]
]
},
{
"id": "43f97b93.b76294",
"type": "inject",
"z": "cb95299c.2817c8",
"name": "1",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "1",
"payloadType": "num",
"x": 270,
"y": 240,
2020-12-03 23:13:08 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"21af0db1.c2c182",
"880b192a.88e2d"
2020-12-03 23:13:08 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "ac439738.28bc18",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "cb95299c.2817c8",
"name": "chart init",
"func": "obj=[{\"series\": [],\"data\": [],\"labels\": []}]\nmsg.payload = obj\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 980,
"y": 880,
"wires": [
[
2021-12-03 02:34:14 +01:00
"458cd82e.03d258"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4f3f7c4a.cb21c4",
"type": "change",
"z": "cb95299c.2817c8",
"name": "",
"rules": [
{
"t": "set",
"p": "obj_counter",
"pt": "flow",
"to": "0",
"tot": "num"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
2021-12-03 02:51:25 +01:00
"x": 1010,
2021-12-03 02:34:14 +01:00
"y": 840,
"wires": [
[
2021-12-03 02:34:14 +01:00
"fa3b7929.ac7da8",
"9d53dbe2.dbffe8"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6bce0f60.f48998",
"type": "link in",
"z": "cb95299c.2817c8",
"name": "",
"links": [
"596fc9d4.46c75"
],
"x": 855,
"y": 840,
"wires": [
[
2021-12-03 02:34:14 +01:00
"4f3f7c4a.cb21c4",
"ac439738.28bc18"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "596fc9d4.46c75",
"type": "link out",
"z": "cb95299c.2817c8",
"name": "",
"links": [
"6bce0f60.f48998"
],
"x": 715,
"y": 500,
"wires": []
2020-12-04 23:04:19 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "c72a1064.1ec388",
"type": "change",
"z": "cb95299c.2817c8",
"name": "Reset counters",
"rules": [
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "object count",
"tot": "str"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "0",
"tot": "num"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 600,
"y": 500,
2020-12-04 23:04:19 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"596fc9d4.46c75"
2020-12-04 23:04:19 +01:00
]
2021-12-03 02:34:14 +01:00
]
2020-12-04 23:04:19 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "9367534a.fb8568",
"type": "ui_multistate_switch",
"z": "cb95299c.2817c8",
"name": "Ecotaxa archive",
"group": "abeb6dad.635a2",
"order": 6,
"width": 5,
"height": 1,
"label": "Ecotaxa archive",
"stateField": "payload",
"enableField": "enable",
"rounded": true,
"useThemeColors": true,
"hideSelectedLabel": false,
"options": [
{
"label": "No",
"value": "0",
"valueType": "num",
"color": "#009933"
},
{
"label": "Yes",
"value": "1",
"valueType": "num",
"color": "#999999"
}
],
"x": 1080,
"y": 220,
2020-12-04 23:04:19 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"25ac4f9a.5b05c8"
2020-12-04 23:04:19 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "25ac4f9a.5b05c8",
"type": "change",
"z": "cb95299c.2817c8",
2020-12-04 23:04:19 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"rules": [
{
"t": "set",
"p": "ecotaxa",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1280,
"y": 240,
2020-12-04 23:04:19 +01:00
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "bef78886.8f0b98",
2020-12-04 23:04:19 +01:00
"type": "inject",
2021-12-03 02:34:14 +01:00
"z": "cb95299c.2817c8",
"name": "1",
2020-12-04 23:04:19 +01:00
"props": [
{
2021-12-03 02:34:14 +01:00
"p": "payload"
2020-12-04 23:04:19 +01:00
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
2021-12-03 02:34:14 +01:00
"payload": "1",
"payloadType": "num",
"x": 910,
"y": 240,
2020-12-04 23:04:19 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"9367534a.fb8568",
"25ac4f9a.5b05c8"
2020-12-04 23:04:19 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "32465a4e.8fcac6",
"type": "ui_multistate_switch",
"z": "cb95299c.2817c8",
"name": "Keep objects",
"group": "abeb6dad.635a2",
"order": 5,
"width": 5,
"height": 1,
"label": "Keep objects",
"stateField": "payload",
"enableField": "enable",
"rounded": true,
"useThemeColors": true,
"hideSelectedLabel": false,
"options": [
{
"label": "No",
"value": "0",
"valueType": "num",
"color": "#009933"
},
{
"label": "Yes",
"value": "1",
"valueType": "num",
"color": "#999999"
}
],
"x": 1070,
"y": 300,
"wires": [
[
"8dd6f57f.b77f98"
]
]
},
{
"id": "8dd6f57f.b77f98",
2020-12-04 23:04:19 +01:00
"type": "change",
2021-12-03 02:34:14 +01:00
"z": "cb95299c.2817c8",
2020-12-04 23:04:19 +01:00
"name": "",
"rules": [
{
"t": "set",
2021-12-03 02:34:14 +01:00
"p": "keep",
"pt": "flow",
2020-12-04 23:04:19 +01:00
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
2021-12-03 02:34:14 +01:00
"x": 1270,
"y": 280,
2020-12-04 23:04:19 +01:00
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "8090df89.c029e",
"type": "inject",
"z": "cb95299c.2817c8",
"name": "1",
"props": [
2020-12-04 23:04:19 +01:00
{
2021-12-03 02:34:14 +01:00
"p": "payload"
2020-12-04 23:04:19 +01:00
}
],
2021-12-03 02:34:14 +01:00
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "1",
"payloadType": "num",
"x": 910,
"y": 280,
2020-12-04 23:04:19 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"32465a4e.8fcac6",
"8dd6f57f.b77f98"
2020-12-04 23:04:19 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "56971109c8be3b54",
"type": "ui_text_input",
"z": "cb95299c.2817c8",
"name": "process_id",
"label": "Process unique ID*",
2020-12-04 23:04:19 +01:00
"tooltip": "",
2021-12-03 02:34:14 +01:00
"group": "abeb6dad.635a2",
"order": 2,
"width": 10,
2020-12-04 23:04:19 +01:00
"height": 1,
2021-12-03 02:34:14 +01:00
"passthru": true,
"mode": "text",
"delay": 300,
"topic": "process_id",
"sendOnBlur": true,
"className": "",
2021-09-06 18:54:34 +02:00
"topicType": "str",
2021-12-03 02:34:14 +01:00
"x": 790,
"y": 380,
2020-12-04 23:04:19 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"c8749cbb.55254"
2020-12-04 23:04:19 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "604659d148759b84",
"type": "link in",
"z": "cb95299c.2817c8",
"name": "Cam Stream",
2020-12-04 23:04:19 +01:00
"links": [
2021-12-03 02:34:14 +01:00
"49111a72f4e32740"
2020-12-04 23:04:19 +01:00
],
2021-12-03 02:34:14 +01:00
"x": 975,
"y": 600,
"wires": [
[
"e077b83ad375cb95"
]
]
2020-12-04 23:04:19 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "e077b83ad375cb95",
"type": "template",
"z": "cb95299c.2817c8",
"name": "Prepare stream HTML",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "<center>\n Latest object segmented:<br/>\n <img src=\"http://{{payload}}:8001/object.mjpg\"\n onerror=\"this.onerror=null; this.src='http://{{payload}}:80/error.svg'\"\n alt=\"If you see this, there probably is an error either with your camera or with the python service. Please restart your machine.\"\n style=\"max-width: 100%;max-height: 100%;\">\n</center>",
"output": "str",
"x": 1120,
"y": 600,
2020-12-04 23:04:19 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"f078c068.eacf58"
2020-12-04 23:04:19 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "2911fbc6.d28c24",
"type": "ui_template",
"z": "c1660bc.e7ff7f8",
"group": "c0ebfc57.42527",
"name": "Image browser",
"order": 1,
"width": 24,
"height": 24,
"format": "<center>\n<object data=\"http://{{msg.payload}}:80/\" width=\"1400px\" height=\"1100px\">\n</object>\n</center>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": false,
"templateScope": "local",
"x": 400,
"y": 100,
"wires": [
[]
]
},
{
"id": "3bb4970164f5e7b2",
2020-12-04 23:04:19 +01:00
"type": "link in",
2021-12-03 02:34:14 +01:00
"z": "c1660bc.e7ff7f8",
"name": "Cam Stream",
2020-12-04 23:04:19 +01:00
"links": [
2021-12-03 02:34:14 +01:00
"49111a72f4e32740"
2020-12-04 23:04:19 +01:00
],
2021-12-03 02:34:14 +01:00
"x": 255,
"y": 100,
2020-12-04 23:04:19 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"2911fbc6.d28c24"
2020-12-04 23:04:19 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4f02c726.de69b8",
"type": "ui_button",
"z": "9daf9e2b.019fc",
"name": "",
"group": "1be83144.4fe4bf",
"order": 10,
"width": 2,
"height": 1,
"passthru": false,
"label": "Reboot",
"tooltip": "",
"color": "",
"bgcolor": "#AD1625",
"icon": "fa-repeat fa-2x",
"payload": "reboot",
"payloadType": "str",
"topic": "reboot",
"x": 420,
"y": 680,
2020-12-04 23:04:19 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"4af9112d.87767"
2020-12-04 23:04:19 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "611a8b1c.a829b4",
"type": "exec",
"z": "9daf9e2b.019fc",
"command": "sudo",
"addpay": "payload",
"append": "now",
"useSpawn": "false",
"timer": "2",
"oldrc": false,
"name": "sudo cmd now",
"x": 740,
"y": 720,
2020-12-04 23:04:19 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[],
[],
2020-12-04 23:04:19 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "bc76c4d6.a7ad28",
"type": "ui_button",
"z": "9daf9e2b.019fc",
"name": "",
"group": "1be83144.4fe4bf",
"order": 12,
"width": 2,
"height": 1,
"passthru": false,
"label": "Shutdown",
"tooltip": "",
"color": "",
"bgcolor": "#AD1625",
"icon": "fa-power-off fa-2x",
"payload": "shutdown",
"payloadType": "str",
"topic": "shutdown",
"x": 420,
"y": 760,
2020-12-04 23:04:19 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"4af9112d.87767"
2020-12-04 23:04:19 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4af9112d.87767",
"type": "python3-function",
"z": "9daf9e2b.019fc",
"name": "action",
"func": "#!/usr/bin/python3\nimport smbus2 as smbus\n\nbus = smbus.SMBus(1)\n#turn off LED\nbus.write_byte_data(0x0d, 0x07, 0x00)\n#turn off Fan\nbus.write_byte_data(0x0d, 0x08, 0x00)\n\n#msg[\"payload\"] = str(msg[\"topic\"])+' now'\nreturn msg",
"outputs": 1,
"x": 590,
"y": 720,
"wires": [
[
"611a8b1c.a829b4"
]
]
},
{
"id": "fa914867.0a0658",
"type": "exec",
"z": "9daf9e2b.019fc",
"command": "python3 -u /home/pi/PlanktoScope/scripts/main.py",
"addpay": false,
"append": "",
"useSpawn": "true",
"timer": "",
2021-12-03 02:04:43 +01:00
"winHide": false,
2021-12-03 02:34:14 +01:00
"oldrc": false,
"name": "",
"x": 1090,
"y": 600,
"wires": [
[],
[
"8b511c2b.9c24c8"
],
[]
]
},
{
"id": "1575db82.742854",
"type": "delay",
"z": "9daf9e2b.019fc",
"name": "",
"pauseType": "delay",
"timeout": "3",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"outputs": 1,
"x": 720,
"y": 600,
2020-12-04 23:04:19 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"fa914867.0a0658"
2020-12-04 23:04:19 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "466eb611.4da048",
"type": "ui_button",
"z": "9daf9e2b.019fc",
"name": "Restart Python",
"group": "1be83144.4fe4bf",
"order": 8,
"width": 4,
"height": 1,
"passthru": true,
"label": "Restart Python",
"tooltip": "",
"color": "",
"bgcolor": "#AD1625",
"icon": "fa-refresh fa-2x",
"payload": "",
"payloadType": "str",
"topic": "",
"x": 400,
"y": 600,
2020-12-04 23:04:19 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"1575db82.742854",
"bd5cceef.b17ad",
"ed7503f3.d95a48"
2020-12-04 23:04:19 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "ed7503f3.d95a48",
"type": "exec",
"z": "9daf9e2b.019fc",
2021-12-03 02:04:43 +01:00
"command": "sudo kill -15 `cat /tmp/pscope_pid`",
2021-12-03 02:34:14 +01:00
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
2021-12-03 02:04:43 +01:00
"winHide": false,
2021-12-03 02:34:14 +01:00
"oldrc": false,
"name": "Python soft kill",
"x": 740,
"y": 660,
2020-12-04 23:04:19 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[],
[],
[]
2020-12-04 23:04:19 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "bd5cceef.b17ad",
"type": "exec",
"z": "9daf9e2b.019fc",
"command": "sudo killall -15 raspimjpeg",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "Raspimjpeg soft kill",
"x": 750,
"y": 540,
2020-12-04 23:04:19 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[],
[],
2020-12-04 23:04:19 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d12008a0.47be4",
2020-12-04 23:04:19 +01:00
"type": "ui_template",
2021-12-03 02:34:14 +01:00
"z": "9daf9e2b.019fc",
"group": "a7d64879.38298",
"name": "Log table",
"order": 1,
"width": 12,
"height": 10,
"format": "<script type=\"text/javascript\">\n function selectElementContents(el) {\n var body = document.body, range, sel;\n if (document.createRange && window.getSelection) {\n range = document.createRange();\n sel = window.getSelection();\n sel.removeAllRanges();\n try {\n range.selectNodeContents(el);\n sel.addRange(range);\n } catch (e) {\n range.selectNode(el);\n sel.addRange(range);\n }\n document.execCommand(\"copy\");\n sel.removeAllRanges();\n\n } else if (body.createTextRange) {\n range = body.createTextRange();\n range.moveToElementText(el);\n range.select();\n range.execCommand(\"Copy\");\n }\n }\n</script>\n\n<md-button onclick=\"selectElementContents( document.getElementById('log_table') );\">\n <i class=\"fa fa-copy fa-1x\"></i> Copy Log\n</md-button>\n\n<table id=\"log_table\" style=\"font-family: mono;font-size: 5px;\">\n <tr ng-repeat=\"row in msg.payload\">\n <td>{{row}}</td>\n </tr>\n</table>\n\n",
"storeOutMessages": false,
"fwdInMessages": false,
"resendOnRefresh": false,
2020-12-04 23:04:19 +01:00
"templateScope": "local",
2021-12-03 02:34:14 +01:00
"x": 1580,
"y": 640,
2020-12-04 23:04:19 +01:00
"wires": [
[]
]
2020-12-05 00:20:44 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "8b511c2b.9c24c8",
2020-12-05 00:20:44 +01:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "9daf9e2b.019fc",
"name": "Log array",
"func": "var log = flow.get(\"python_log\")||[];\n\nlog = log.concat(msg.payload.trim().split('\\n'));\n\nwhile (log.length > 500){\n log.shift();\n}\n\nflow.set(\"python_log\", log);\n\nmsg.payload = log;\nreturn msg;",
2020-12-05 00:20:44 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
2021-12-03 02:34:14 +01:00
"x": 1400,
"y": 560,
2020-12-05 00:20:44 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"d12008a0.47be4"
2020-12-05 00:20:44 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "7a1dedf.1c63094",
"type": "ui_button",
"z": "9daf9e2b.019fc",
"name": "",
"group": "a7d64879.38298",
"order": 2,
"width": 0,
"height": 0,
"passthru": true,
"label": "Clear Log",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "fa-eraser",
"payload": "",
"payloadType": "str",
"topic": "",
"topicType": "str",
"x": 1220,
"y": 720,
2020-12-05 00:20:44 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"73500be7.9ab7c4"
2020-12-05 00:20:44 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "9187bf97.9922b8",
"type": "catch",
"z": "9daf9e2b.019fc",
"name": "Catch Errors",
"scope": null,
"uncaught": false,
"x": 1050,
"y": 660,
2020-12-05 00:20:44 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"f9a871d5.a73828"
2020-12-05 00:20:44 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "f9a871d5.a73828",
2020-12-05 00:20:44 +01:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "9daf9e2b.019fc",
"name": "Get error",
"func": "msg.payload = \"Node Red Error: \" + msg.error + \" created by the message \" + msg.payload;\nreturn msg;",
2020-12-05 00:20:44 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 1220,
"y": 660,
2020-12-05 00:20:44 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"8b511c2b.9c24c8"
2020-12-05 00:20:44 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "73500be7.9ab7c4",
2020-12-05 00:20:44 +01:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "9daf9e2b.019fc",
"name": "Clear log",
"func": "flow.set(\"python_log\", [\"Log cleared!\"]);\n\nmsg.payload = [\"Log cleared!\"];\nreturn msg;",
2020-12-05 00:20:44 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
2021-12-03 02:34:14 +01:00
"x": 1400,
"y": 720,
2020-12-05 00:20:44 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"d12008a0.47be4"
2020-12-05 00:20:44 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "7372e83e.5fc0e8",
"type": "inject",
"z": "9daf9e2b.019fc",
"name": "once",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "2",
"topic": "",
"payload": "start",
"payloadType": "str",
"x": 150,
"y": 600,
2020-12-05 00:20:44 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"466eb611.4da048"
2020-12-05 00:20:44 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "43cf8ff7.75231",
"type": "rpi-gpio out",
"z": "9daf9e2b.019fc",
"name": "Pump Enable",
"pin": "4",
"set": true,
"level": "1",
"freq": "",
"out": "out",
"bcm": true,
"x": 420,
"y": 80,
"wires": []
2020-12-05 00:20:44 +01:00
},
2020-12-05 03:18:43 +01:00
{
2021-12-03 02:34:14 +01:00
"id": "4bca8a25.15be3c",
"type": "rpi-gpio out",
"z": "9daf9e2b.019fc",
"name": "Focus Enable",
"pin": "12",
"set": true,
"level": "1",
"freq": "",
"out": "out",
"bcm": true,
"x": 420,
"y": 120,
"wires": []
},
{
"id": "50f9b5b.a84bccc",
2020-12-05 03:18:43 +01:00
"type": "inject",
"z": "9daf9e2b.019fc",
2021-12-03 02:34:14 +01:00
"name": "Default: ON",
2020-12-05 03:18:43 +01:00
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
2021-12-03 02:34:14 +01:00
"onceDelay": "0.01",
2020-12-05 03:18:43 +01:00
"topic": "",
2021-12-03 02:34:14 +01:00
"payload": "true",
"payloadType": "bool",
"x": 150,
"y": 100,
2020-12-05 03:18:43 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"43cf8ff7.75231",
"4bca8a25.15be3c",
"6db0fcf5.f0effc",
"5fc4ede4.72516c"
2020-12-05 03:18:43 +01:00
]
]
2020-12-08 16:48:50 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "8bd1f3dc.9affe8",
"type": "exec",
"z": "9daf9e2b.019fc",
"command": "",
"addpay": true,
"append": "",
"useSpawn": "false",
"timer": "1",
"oldrc": false,
"name": "git user.name",
"x": 1160,
"y": 60,
"wires": [
[],
[],
[]
]
},
{
"id": "b40d1f65.39e14",
"type": "template",
"z": "9daf9e2b.019fc",
"name": "git config",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "git config --global --replace-all user.name \"{{payload}}\"",
"output": "str",
"x": 960,
"y": 60,
2020-12-08 16:48:50 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"8bd1f3dc.9affe8"
2020-12-08 16:48:50 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "35eb925e.5f8016",
"type": "link in",
"z": "9daf9e2b.019fc",
"name": "Git config",
"links": [
"52af9ac0.60eb24"
],
"x": 795,
"y": 100,
"wires": [
[
"b40d1f65.39e14",
"6d7dd372.3b7814"
]
]
},
{
"id": "4e5183bd.e77bac",
2020-12-08 16:48:50 +01:00
"type": "exec",
2021-12-03 02:34:14 +01:00
"z": "9daf9e2b.019fc",
2020-12-08 16:48:50 +01:00
"command": "",
"addpay": true,
"append": "",
"useSpawn": "false",
2021-12-03 02:34:14 +01:00
"timer": "1",
2020-12-08 16:48:50 +01:00
"oldrc": false,
2021-12-03 02:34:14 +01:00
"name": "git user.email",
"x": 1150,
"y": 140,
2020-12-08 16:48:50 +01:00
"wires": [
[],
[],
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6d7dd372.3b7814",
"type": "template",
"z": "9daf9e2b.019fc",
"name": "git config",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "git config --global --replace-all user.email \"{{payload}}\"",
"output": "str",
"x": 960,
"y": 140,
"wires": [
[
"4e5183bd.e77bac"
]
]
},
{
"id": "e564e9ad.7c312",
2020-12-08 16:48:50 +01:00
"type": "ui_button",
2021-12-03 02:34:14 +01:00
"z": "9daf9e2b.019fc",
"d": true,
"name": "Update",
"group": "1be83144.4fe4bf",
"order": 13,
"width": 4,
"height": 1,
2020-12-08 16:48:50 +01:00
"passthru": false,
2021-12-03 02:34:14 +01:00
"label": "System Update",
2020-12-08 16:48:50 +01:00
"tooltip": "",
"color": "",
2021-12-03 02:34:14 +01:00
"bgcolor": "",
"icon": "mi-system_update",
"payload": " ",
"payloadType": "str",
"topic": "update",
"topicType": "str",
"x": 260,
2020-12-08 16:48:50 +01:00
"y": 460,
"wires": [
2021-12-03 02:34:14 +01:00
[]
2020-12-08 16:48:50 +01:00
]
2020-12-09 15:27:12 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "2d2ef1fd.40e6e6",
"type": "exec",
"z": "9daf9e2b.019fc",
"command": "bash /home/pi/PlanktoScope/scripts/bash/start_update.sh",
"addpay": true,
"append": "",
"useSpawn": "true",
"timer": "",
"oldrc": false,
"name": "Update",
"x": 420,
"y": 460,
2020-12-09 15:27:12 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"64f61762.68c788",
"8b511c2b.9c24c8",
"83c5a708.a5715",
"d334d264.8a7728"
],
[
"8b511c2b.9c24c8"
],
[]
2020-12-09 15:27:12 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "8f08c4eb.db74b8",
"type": "ui_template",
"z": "9daf9e2b.019fc",
"group": "1be83144.4fe4bf",
"name": "Update message",
"order": 1,
"width": 12,
"height": 3,
"format": "<div><center>If you want to update to the latest code version of Node-Red and Python, please click here.\nWe are trying to save your configuration changes with git stash and restore them afterwards, but clearly, things may break!\nAlso, you can choose the branch you want to update to. BE CAREFUL, selecting a branch WILL update the code immediately.\nIf you have made changes to the flow, you will lose them. Please commit those changes from Node-Red interface, from the Project History tab.</center></div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"x": 150,
"y": 260,
2020-12-09 15:27:12 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[]
2020-12-09 15:27:12 +01:00
]
2020-12-17 19:44:06 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "64f61762.68c788",
"type": "ui_toast",
"z": "9daf9e2b.019fc",
"position": "dialog",
"displayTime": "10",
"highlight": "",
"sendall": true,
"outputs": 1,
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
"name": "Update notif",
"x": 730,
"y": 440,
2020-12-17 19:44:06 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[]
2020-12-17 19:44:06 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "45a7b5aa.2ed20c",
"type": "link in",
"z": "9daf9e2b.019fc",
"name": "Python restart",
"links": [
"e41870d7.300eb8"
],
"x": 175,
"y": 560,
"wires": [
[
2021-12-03 02:34:14 +01:00
"466eb611.4da048"
]
]
2021-01-15 11:48:27 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "f4f6e3fa.0234f",
"type": "inject",
"z": "9daf9e2b.019fc",
"name": "once",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "",
"topic": "",
"payload": "start",
"payloadType": "str",
"x": 1070,
"y": 720,
2021-01-15 11:48:27 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"7a1dedf.1c63094"
2021-01-15 11:48:27 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "b81b990a.d4dca",
"type": "link in",
"z": "9daf9e2b.019fc",
"name": "Shutdown",
"links": [
"e08cfcb8.2a67e8"
],
"x": 455,
"y": 720,
2021-01-15 11:48:27 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"4af9112d.87767"
2021-01-15 11:48:27 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "83c5a708.a5715",
"type": "exec",
"z": "9daf9e2b.019fc",
"command": "git --git-dir=/home/pi/PlanktoScope/.git describe --long --tags",
"addpay": false,
"append": "",
"useSpawn": "true",
"timer": "",
"oldrc": false,
"name": "Get git revision",
"x": 660,
"y": 340,
2021-01-15 11:48:27 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"af2b8d95.195bb8",
"b6bc9b81.ff942"
],
[],
[]
2021-01-15 11:48:27 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d334d264.8a7728",
"type": "exec",
"z": "9daf9e2b.019fc",
"command": "git --git-dir=/home/pi/PlanktoScope/.git branch --remotes --list | awk '/HEAD/{next;} split($1, a, \"/\") {print a[2]}'",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "Get git branch",
"x": 660,
"y": 260,
"wires": [
[
"e5e8e66c.899588"
],
[],
[]
]
},
{
"id": "e4342b66.72972",
"type": "ui_button",
"z": "9daf9e2b.019fc",
"name": "get git branch",
"group": "1be83144.4fe4bf",
"order": 3,
"width": 5,
"height": 1,
2021-01-15 12:16:44 +01:00
"passthru": false,
2021-12-03 02:34:14 +01:00
"label": "Update branch list",
2021-01-15 12:16:44 +01:00
"tooltip": "",
"color": "",
"bgcolor": "",
2021-12-03 02:34:14 +01:00
"icon": "mi-compare_arrows",
"payload": "",
"payloadType": "str",
2021-01-15 12:16:44 +01:00
"topic": "",
2021-12-03 02:34:14 +01:00
"x": 420,
"y": 260,
2021-01-15 12:16:44 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"d334d264.8a7728"
2021-01-15 12:16:44 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "3f13b9f8.bac94e",
"type": "ui_dropdown",
"z": "9daf9e2b.019fc",
"name": "Git branch list",
"label": "Switch branch to",
"tooltip": "If you choose something, the code WILL be updated to that branch, don't touch if you don't know what you are doing!",
"place": "branch",
"group": "1be83144.4fe4bf",
"order": 4,
"width": 7,
"height": 1,
"passthru": false,
"multiple": false,
"options": [],
"payload": "",
"topic": "branch",
"x": 1160,
"y": 320,
"wires": [
[]
]
},
{
"id": "e5e8e66c.899588",
"type": "function",
"z": "9daf9e2b.019fc",
"name": "filter branch",
"func": "msg.options = msg.payload.trimEnd().split('\\n')\nmsg.payload = msg.options.find((str) => str.startsWith('*'));\nreturn msg;",
2021-01-15 12:16:44 +01:00
"outputs": 1,
2021-12-03 02:34:14 +01:00
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 870,
"y": 260,
2021-01-15 12:16:44 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"3f13b9f8.bac94e",
"ccc7d79b.a419f"
2021-01-15 12:16:44 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "c1fb03f7.e675f8",
2021-01-15 12:16:44 +01:00
"type": "inject",
2021-12-03 02:34:14 +01:00
"z": "9daf9e2b.019fc",
"name": "once",
2021-01-15 12:16:44 +01:00
"props": [
{
2021-12-03 02:34:14 +01:00
"p": "payload"
2021-01-15 12:16:44 +01:00
}
],
"repeat": "",
"crontab": "",
"once": true,
2021-12-03 02:34:14 +01:00
"onceDelay": "",
2021-01-15 12:16:44 +01:00
"topic": "",
2021-12-03 02:34:14 +01:00
"payload": "start",
2021-01-15 12:16:44 +01:00
"payloadType": "str",
2021-12-03 02:34:14 +01:00
"x": 430,
"y": 300,
2021-01-15 12:16:44 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"d334d264.8a7728",
"83c5a708.a5715"
2021-01-15 12:16:44 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "af2b8d95.195bb8",
"type": "ui_text",
"z": "9daf9e2b.019fc",
"group": "1be83144.4fe4bf",
"order": 2,
"width": 12,
"height": 1,
2021-01-15 12:16:44 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"label": "Current code version",
"format": "{{msg.payload}}",
"layout": "row-center",
"x": 900,
"y": 320,
"wires": []
2021-01-15 12:16:44 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "b6bc9b81.ff942",
2021-01-15 12:16:44 +01:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "9daf9e2b.019fc",
"name": "save process_commit",
"func": "global.set(\"process_commit\", msg.payload);\n",
2021-01-15 12:16:44 +01:00
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 900,
"y": 360,
2021-01-15 12:16:44 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"6c20d6a7.869b6"
2021-01-15 12:16:44 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6c20d6a7.869b6",
2021-01-15 12:16:44 +01:00
"type": "link out",
2021-12-03 02:34:14 +01:00
"z": "9daf9e2b.019fc",
"name": "Git revision",
2021-01-15 12:16:44 +01:00
"links": [
2021-12-03 02:34:14 +01:00
"9373b3f6.33a2b"
2021-01-15 12:16:44 +01:00
],
2021-12-03 02:34:14 +01:00
"x": 1035,
"y": 360,
2021-01-15 12:16:44 +01:00
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "7de3a9b4.0ab108",
2021-01-15 12:16:44 +01:00
"type": "ui_toast",
2021-12-03 02:34:14 +01:00
"z": "9daf9e2b.019fc",
"position": "top right",
2021-01-15 12:16:44 +01:00
"displayTime": "10",
"highlight": "",
"sendall": true,
2021-12-03 02:34:14 +01:00
"outputs": 0,
2021-01-15 12:16:44 +01:00
"ok": "OK",
"cancel": "",
"raw": false,
2021-12-03 02:34:14 +01:00
"topic": "",
"name": "Git branch update",
"x": 1290,
"y": 260,
"wires": []
2021-01-15 12:16:44 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "ccc7d79b.a419f",
2021-01-15 12:16:44 +01:00
"type": "change",
2021-12-03 02:34:14 +01:00
"z": "9daf9e2b.019fc",
"name": "",
2021-01-15 12:16:44 +01:00
"rules": [
{
"t": "set",
2021-12-03 02:34:14 +01:00
"p": "payload",
"pt": "msg",
"to": "List update successfull",
"tot": "str"
},
{
"t": "set",
"p": "topic",
2021-01-15 12:16:44 +01:00
"pt": "msg",
2021-12-03 02:34:14 +01:00
"to": "Branch list update",
2021-01-15 12:16:44 +01:00
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
2021-12-03 02:34:14 +01:00
"x": 1080,
"y": 260,
2021-01-15 12:16:44 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"7de3a9b4.0ab108"
2021-01-15 12:16:44 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "5fc4ede4.72516c",
"type": "rpi-gpio out",
"z": "9daf9e2b.019fc",
"name": "HAT Pump Enable",
"pin": "23",
"set": true,
"level": "1",
"freq": "",
"out": "out",
"bcm": true,
"x": 430,
"y": 40,
"wires": []
},
{
"id": "6db0fcf5.f0effc",
"type": "rpi-gpio out",
"z": "9daf9e2b.019fc",
"name": "HAT Focus Enable",
"pin": "5",
"set": true,
"level": "1",
"freq": "",
"out": "out",
"bcm": true,
"x": 430,
"y": 160,
"wires": []
},
{
"id": "2c8c45ab.610c0a",
"type": "ui_gauge",
"z": "1371dec5.76e671",
2021-01-15 12:16:44 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"group": "b5ba3f26.2e722",
"order": 1,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "CPU Temperature",
"label": "°C",
"format": "{{value}}",
"min": "30",
"max": "55",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
2021-01-15 12:16:44 +01:00
],
2021-12-03 02:34:14 +01:00
"seg1": "",
"seg2": "",
"x": 610,
"y": 100,
"wires": []
},
{
"id": "ddcbbfa5.cd158",
"type": "exec",
"z": "1371dec5.76e671",
"command": "vcgencmd measure_temp | tr -d \"temp=\" | tr -d \"'C\" | tr -d \"\\n\"",
"addpay": false,
"append": "",
"useSpawn": "",
"timer": "",
"name": "RPi Temp.",
"x": 330,
"y": 120,
2021-01-15 12:16:44 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"bc503fa5.f46dd",
"2c8c45ab.610c0a",
"cef370c7.1f7a"
2021-01-15 12:16:44 +01:00
],
2021-12-03 02:34:14 +01:00
[],
[]
2021-01-15 12:16:44 +01:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "cef370c7.1f7a",
"type": "ui_chart",
"z": "1371dec5.76e671",
"name": "",
"group": "b5ba3f26.2e722",
"order": 2,
"width": 0,
"height": 0,
"label": "",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "30",
"ymax": "70",
"removeOlder": "20",
"removeOlderPoints": "200",
"removeOlderUnit": "60",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
2021-01-15 12:16:44 +01:00
],
2021-12-03 02:34:14 +01:00
"outputs": 1,
"useDifferentColor": false,
"x": 570,
"y": 140,
2021-01-15 12:16:44 +01:00
"wires": [
2021-12-03 02:34:14 +01:00
[]
2021-01-15 12:16:44 +01:00
]
2021-05-06 03:11:45 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "55749fd8.3aca5",
"type": "exec",
"z": "1371dec5.76e671",
"command": "top -d 0.5 -b -n2 | grep \"Cpu(s)\"|tail -n 1 | awk '{print $2 + $4}' | tr -d \"\\n\"",
"addpay": false,
"append": "",
"useSpawn": "",
"timer": "",
"name": "CPU Load",
"x": 330,
"y": 200,
2021-05-06 03:11:45 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"e046247c.c466e8",
"4b2b9a70.6a9af4"
],
[],
[]
2021-05-06 03:11:45 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "c2b8d8b.0d90328",
"type": "exec",
"z": "1371dec5.76e671",
"command": "free | grep Mem | awk '{print 100*($2-$7)/$2}' | awk '{print int($1+0.5)}' | tr -d \"\\n\"",
"addpay": false,
"append": "",
"useSpawn": "",
"timer": "",
"name": "Free Memory",
"x": 330,
"y": 280,
2021-05-06 03:11:45 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"e84f6e08.5d147",
"c7c8d192.3df27"
],
[],
[]
2021-05-06 03:11:45 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "e046247c.c466e8",
"type": "ui_gauge",
"z": "1371dec5.76e671",
"name": "",
"group": "3da7da8f.179606",
"order": 1,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Processor",
"label": "%",
"format": "{{value}}",
"min": 0,
"max": "100",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
2021-12-03 02:51:25 +01:00
"x": 580,
2021-12-03 02:34:14 +01:00
"y": 180,
"wires": []
},
{
"id": "e84f6e08.5d147",
"type": "ui_gauge",
"z": "1371dec5.76e671",
2021-05-06 03:11:45 +02:00
"name": "",
2021-12-03 02:34:14 +01:00
"group": "806d69c8.67fc58",
"order": 1,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Memory",
"label": "%",
"format": "{{value}}",
"min": 0,
"max": "100",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"x": 580,
2021-12-03 02:34:14 +01:00
"y": 260,
"wires": []
2021-05-06 03:11:45 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "3910d662.fa1f7a",
"type": "exec",
"z": "1371dec5.76e671",
"command": "df -h | grep /dev/root | awk -F ' ' '{print $5}' | tr -d % | tr \"\\n$\" \"\\ \" | sed 's/,/./' | tr -d \" \"",
"addpay": false,
"append": "",
"useSpawn": "",
"timer": "",
"name": "Disk Usage",
2021-05-06 03:11:45 +02:00
"x": 330,
"y": 360,
"wires": [
[
2021-12-03 02:34:14 +01:00
"45e1912a.36a23",
"84ac8611.1a6ac8",
"eaf74a43.fa27d"
],
[],
[]
2021-05-06 03:11:45 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "45e1912a.36a23",
"type": "ui_gauge",
"z": "1371dec5.76e671",
"name": "",
"group": "405183bc.d8991c",
"order": 1,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Disk",
"label": "%",
"format": "{{value}}",
"min": 0,
"max": "100",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"x": 570,
"y": 340,
"wires": []
2021-05-06 03:11:45 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "4b2b9a70.6a9af4",
"type": "ui_chart",
"z": "1371dec5.76e671",
"name": "",
2021-12-03 02:34:14 +01:00
"group": "3da7da8f.179606",
"order": 2,
"width": 0,
"height": 0,
"label": "",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": "20",
"removeOlderPoints": "",
"removeOlderUnit": "60",
"cutout": 0,
"useOneColor": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
2021-12-03 02:34:14 +01:00
"x": 570,
"y": 220,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "c7c8d192.3df27",
"type": "ui_chart",
"z": "1371dec5.76e671",
"name": "",
2021-12-03 02:34:14 +01:00
"group": "806d69c8.67fc58",
"order": 2,
"width": 0,
"height": 0,
"label": "",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "0",
"ymax": "100",
"removeOlder": "20",
"removeOlderPoints": "",
"removeOlderUnit": "60",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"x": 570,
"y": 300,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "84ac8611.1a6ac8",
"type": "ui_chart",
"z": "1371dec5.76e671",
"name": "",
"group": "405183bc.d8991c",
"order": 2,
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"label": "",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "0",
"ymax": "100",
"removeOlder": "4",
"removeOlderPoints": "200",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"x": 570,
"y": 380,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "1cd5b4c0.46af9b",
"type": "inject",
2021-12-03 02:34:14 +01:00
"z": "1371dec5.76e671",
"name": "update: 15s",
"props": [
{
"p": "payload"
2021-12-03 02:34:14 +01:00
},
{
"p": "topic",
"vt": "str"
}
],
2021-12-03 02:34:14 +01:00
"repeat": "5",
"crontab": "",
2021-12-03 02:34:14 +01:00
"once": false,
"onceDelay": "",
"topic": "",
"payload": "",
"payloadType": "date",
2021-12-03 02:34:14 +01:00
"x": 100,
"y": 240,
"wires": [
[
2021-12-03 02:34:14 +01:00
"3910d662.fa1f7a",
"c2b8d8b.0d90328",
"55749fd8.3aca5",
"ddcbbfa5.cd158"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "8485d05.444903",
"type": "ui_switch",
"z": "1371dec5.76e671",
"name": "fan_state",
"label": "Fan",
"tooltip": "",
2021-12-03 02:34:14 +01:00
"group": "3ca00bf9.e5cac4",
"order": 2,
"width": 2,
"height": 1,
"passthru": true,
"decouple": "false",
"topic": "",
2021-12-03 02:34:14 +01:00
"topicType": "str",
"style": "",
"onvalue": "on",
"onvalueType": "str",
"onicon": "",
"oncolor": "",
"offvalue": "off",
"offvalueType": "str",
"officon": "",
"offcolor": "",
"animate": true,
"x": 880,
"y": 40,
"wires": [
2021-12-03 02:51:25 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "2549f778.4eb828",
"type": "python3-function",
"z": "1371dec5.76e671",
"name": "fan control",
"func": "import smbus2 as smbus\n\nstate = msg[\"payload\"]\nbus = smbus.SMBus(1)\n\nDEVICE_ADDRESS = 0x0d\n# command happens twice, for reasons\nif state == \"off\":\n with smbus.SMBus(1) as bus:\n bus.write_byte_data(DEVICE_ADDRESS, 0x08, 0x00)\n bus.write_byte_data(DEVICE_ADDRESS, 0x08, 0x00)\n return msg\nif state == \"on\":\n with smbus.SMBus(1) as bus:\n bus.write_byte_data(DEVICE_ADDRESS, 0x08, 0x01)\n bus.write_byte_data(DEVICE_ADDRESS, 0x08, 0x01)\n return msg\n",
"outputs": 1,
2021-12-03 02:34:14 +01:00
"x": 1150,
"y": 40,
"wires": [
[
2021-12-03 02:34:14 +01:00
"e640ebb5c397f424"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "b7ab1ada.1f4158",
"type": "exec",
"z": "1371dec5.76e671",
"command": "i2cdetect -y 1",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "1",
"winHide": false,
"oldrc": false,
"name": "i2c update",
"x": 1440,
"y": 40,
"wires": [
2021-12-03 02:34:14 +01:00
[],
[],
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "bc503fa5.f46dd",
"type": "switch",
"z": "1371dec5.76e671",
"name": "",
2021-12-03 02:34:14 +01:00
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "gt",
"v": "40",
"vt": "num"
},
{
2021-12-03 02:34:14 +01:00
"t": "lte",
"v": "35",
"vt": "num"
2021-05-06 03:11:45 +02:00
}
],
2021-12-03 02:34:14 +01:00
"checkall": "true",
"repair": false,
"outputs": 2,
"x": 570,
"y": 40,
2021-05-06 03:11:45 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"a25d6486.e1ce28"
],
[
"5d4f3e71.1bad4"
2021-05-06 03:11:45 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "5d4f3e71.1bad4",
"type": "change",
"z": "1371dec5.76e671",
"name": "Set OFF",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "off",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 720,
"y": 60,
"wires": [
2021-12-03 02:34:14 +01:00
[
"8485d05.444903"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "a25d6486.e1ce28",
"type": "change",
2021-12-03 02:34:14 +01:00
"z": "1371dec5.76e671",
"name": "Set ON",
"rules": [
{
"t": "set",
2021-12-03 02:34:14 +01:00
"p": "payload",
"pt": "msg",
"to": "on",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
2021-12-03 02:34:14 +01:00
"x": 720,
"y": 20,
"wires": [
2021-12-03 02:34:14 +01:00
[
"8485d05.444903"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "569154a.b53182c",
"type": "inject",
"z": "1371dec5.76e671",
"name": "once",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "",
"topic": "",
"payload": "start",
"payloadType": "str",
"x": 130,
"y": 620,
"wires": [
2021-12-03 02:34:14 +01:00
[
"452af41c.43940c"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "452af41c.43940c",
"type": "python3-function",
"z": "1371dec5.76e671",
2021-12-03 02:34:14 +01:00
"name": "Get MachineName",
"func": "import sys\nsys.path.append('/home/pi/PlanktoScope/scripts')\nimport planktoscope.uuidName\n\nmsg['payload'] = planktoscope.uuidName.machineName(machine=planktoscope.uuidName.getSerial())\nmsg['topic'] = \"acq_instrument_id\";\nreturn msg",
"outputs": 1,
"x": 350,
"y": 620,
"wires": [
[
2021-12-03 02:34:14 +01:00
"4828d2f4.7c712c",
"a400a97e.e333a8"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4828d2f4.7c712c",
"type": "function",
"z": "1371dec5.76e671",
2021-12-03 02:34:14 +01:00
"name": "set global",
"func": "global.set(msg.topic,msg.payload);\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 580,
"y": 640,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "e2f39a35.f57298",
"type": "subflow:1c24ad9c.bebec2",
"z": "1371dec5.76e671",
2021-12-03 02:34:14 +01:00
"name": "",
"env": [],
"x": 570,
"y": 560,
"wires": [
[
2021-12-03 02:34:14 +01:00
"bdc6718a.dd5d48"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "3e64877a.9684b",
"type": "function",
"z": "1371dec5.76e671",
2021-12-03 02:34:14 +01:00
"name": "store version",
"func": "msg.payload = \"PlanktoScope v2.3-\"+msg.payload.trim()\n\nglobal.set(\"acq_software\",msg.payload);\n\nreturn msg\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 590,
"y": 520,
"wires": [
[
2021-12-03 02:34:14 +01:00
"8343fa69.49339"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "bdc6718a.dd5d48",
"type": "ui_text",
"z": "1371dec5.76e671",
2021-12-03 02:34:14 +01:00
"group": "ce9e278.781eed8",
"order": 2,
2021-12-03 02:34:14 +01:00
"width": 0,
"height": 0,
"name": "",
"label": "Instrument Type",
"format": "{{msg.payload.acq_instrument}}",
"layout": "row-spread",
"x": 840,
"y": 560,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "a400a97e.e333a8",
"type": "ui_text",
"z": "1371dec5.76e671",
2021-12-03 02:34:14 +01:00
"group": "ce9e278.781eed8",
"order": 1,
"width": 0,
"height": 0,
"name": "",
2021-12-03 02:34:14 +01:00
"label": "Instrument Name",
"format": "{{msg.payload}}",
"layout": "row-spread",
"x": 610,
"y": 600,
"wires": []
},
{
"id": "8343fa69.49339",
"type": "ui_text",
"z": "1371dec5.76e671",
"group": "ce9e278.781eed8",
"order": 3,
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"name": "",
"label": "Software version",
"format": "{{msg.payload}}",
"layout": "row-spread",
"x": 840,
"y": 520,
"wires": []
},
{
"id": "f783aefd.c3bfd8",
"type": "ui_text",
"z": "1371dec5.76e671",
"group": "ce9e278.781eed8",
"order": 4,
"width": 0,
"height": 0,
"name": "",
"label": "Camera Name",
"format": "{{msg.payload}}",
"layout": "row-spread",
"x": 1020,
"y": 620,
"wires": []
},
{
"id": "30067f35.532f2",
"type": "link in",
"z": "1371dec5.76e671",
"name": "Camera Name",
"links": [
"559a8085.1d6b9"
],
"x": 875,
"y": 620,
"wires": [
[
2021-12-03 02:34:14 +01:00
"f783aefd.c3bfd8"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "11b51f8f.acd308",
"type": "python3-function",
"z": "1371dec5.76e671",
2021-12-03 02:34:14 +01:00
"d": true,
"name": "fan temperature",
"func": "import smbus\nbus = smbus.SMBus(1)\n\naddr = 0x0d\nfan_reg = 0x08\n\ntemp = float( msg[\"payload\"])\n\n# 0x01 full speed, 0x02: 20% speed, ..., 0x09: 90% speed\n\nif temp < 38:\n bus.write_byte_data(addr, fan_reg, 0x00)\nelif temp < 43:\n bus.write_byte_data(addr, fan_reg, 0x02)\nelif temp < 46:\n bus.write_byte_data(addr, fan_reg, 0x04)\nelif temp < 48:\n bus.write_byte_data(addr, fan_reg, 0x06)\nelif temp < 52:\n bus.write_byte_data(addr, fan_reg, 0x08)\nelse:\n bus.write_byte_data(addr, fan_reg, 0x01)\n\nreturn msg",
"outputs": 1,
"x": 1440,
"y": 120,
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "eaf74a43.fa27d",
"type": "switch",
"z": "1371dec5.76e671",
2021-12-03 02:34:14 +01:00
"name": "if disk > 90%",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "gte",
"v": "90",
"vt": "num"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 590,
"y": 420,
"wires": [
[
"6d09f428.930bcc"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "46d5e78b.15f998",
"type": "ui_toast",
"z": "1371dec5.76e671",
2021-12-03 02:34:14 +01:00
"position": "dialog",
"displayTime": "10",
"highlight": "",
"sendall": true,
"outputs": 1,
2021-12-03 02:34:14 +01:00
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
"name": "",
"x": 1090,
"y": 420,
"wires": [
[]
]
},
{
"id": "24df077a.cb252",
"type": "change",
"z": "1371dec5.76e671",
"name": "full disk msg",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "\"Your drive has reached 90% capacity, you should do a backup to an external drive and a purge.\"",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 930,
"y": 420,
"wires": [
[
2021-12-03 02:34:14 +01:00
"46d5e78b.15f998"
]
]
},
2021-09-06 18:39:52 +02:00
{
2021-12-03 02:34:14 +01:00
"id": "f344dc9e.3c9278",
"type": "ui_button",
"z": "1371dec5.76e671",
"name": "",
"group": "3ca00bf9.e5cac4",
"order": 1,
"width": 4,
"height": 1,
"passthru": false,
"label": "Home",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "home",
"payload": "{\"tab\":\"Home\"}",
"payloadType": "json",
"topic": "",
"x": 430,
"y": 740,
2021-09-06 18:39:52 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"5f94b366.e4d3a4"
2021-09-06 18:39:52 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "5f94b366.e4d3a4",
"type": "ui_ui_control",
"z": "1371dec5.76e671",
"name": "",
"events": "change",
"x": 580,
"y": 740,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "f30bf784.90d58",
"type": "ui_dropdown",
"z": "1371dec5.76e671",
"name": "",
"label": "",
"tooltip": "",
"place": "Choose USB device",
"group": "52d1b77.28369c8",
"order": 2,
"width": 0,
"height": 0,
"passthru": false,
"multiple": false,
"options": [
{
2021-12-03 02:34:14 +01:00
"label": "",
"value": "No Drive Detected",
"type": "str"
}
],
2021-12-03 02:34:14 +01:00
"payload": "",
"topic": "device_path",
"x": 840,
"y": 820,
"wires": [
[
2021-12-03 02:34:14 +01:00
"d1b9fed3.f3ead8"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "fe29acc2.c25d18",
"type": "ui_button",
"z": "1371dec5.76e671",
"name": "Detect drive",
"group": "52d1b77.28369c8",
"order": 1,
"width": 0,
"height": 0,
"passthru": false,
"label": "Detect drive",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "fa-usb fa-2x",
"payload": "",
"payloadType": "str",
"topic": "",
"x": 150,
"y": 820,
"wires": [
[
2021-12-03 02:34:14 +01:00
"4c00cd96.6b2f6c"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4c00cd96.6b2f6c",
"type": "exec",
"z": "1371dec5.76e671",
"command": "lsblk --bytes --json --paths --output-all",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "detect drive",
"x": 330,
"y": 820,
"wires": [
[
"2dc1e43d.cd3d64"
],
[],
[]
]
},
{
"id": "2dc1e43d.cd3d64",
"type": "json",
"z": "1371dec5.76e671",
"name": "",
2021-12-03 02:34:14 +01:00
"property": "payload",
"action": "",
2021-12-03 02:34:14 +01:00
"pretty": false,
"x": 490,
"y": 820,
"wires": [
2021-12-03 02:34:14 +01:00
[
"6b730649.09351"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6b730649.09351",
"type": "change",
2021-12-03 02:34:14 +01:00
"z": "1371dec5.76e671",
"name": "",
"rules": [
{
"t": "set",
2021-12-03 02:34:14 +01:00
"p": "options",
"pt": "msg",
"to": "[payload.blockdevices.children[mountpoint=null].name]",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
2021-12-03 02:34:14 +01:00
"x": 660,
"y": 820,
"wires": [
[
2021-12-03 02:34:14 +01:00
"f30bf784.90d58"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "1ece8d8e.1008e2",
"type": "ui_button",
"z": "1371dec5.76e671",
"name": "Backup to USB",
"group": "52d1b77.28369c8",
"order": 3,
"width": 0,
"height": 0,
"passthru": false,
"label": "Backup to USB",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "archive",
"payload": "",
"payloadType": "str",
"topic": "USB Backup",
"x": 160,
"y": 900,
"wires": [
2021-12-03 02:34:14 +01:00
[
"6b2fed77.883c0c"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d1b9fed3.f3ead8",
"type": "function",
"z": "1371dec5.76e671",
"name": "set local",
"func": "flow.set(msg.topic,msg.payload);",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 1000,
"y": 820,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6b2fed77.883c0c",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "1371dec5.76e671",
"name": "get device_path",
"func": "msg.payload = flow.get(\"device_path\");\nreturn msg",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 340,
"y": 900,
"wires": [
[
2021-12-03 02:34:14 +01:00
"df4d4d6a.24787"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "c74fe76.bcba718",
"type": "exec",
"z": "1371dec5.76e671",
"command": "sudo /home/pi/PlanktoScope/scripts/bash/usb_backup.sh",
"addpay": true,
"append": "",
"useSpawn": "true",
"timer": "",
"oldrc": false,
"name": "usb_backup.sh",
"x": 720,
"y": 920,
"wires": [
[
"72f5e8d4.40fcb8"
],
[],
[
"a2c36500.6d376"
]
]
},
{
"id": "df4d4d6a.24787",
"type": "switch",
"z": "1371dec5.76e671",
"name": "No Drive",
"property": "payload",
"propertyType": "msg",
"rules": [
{
2021-12-03 02:34:14 +01:00
"t": "eq",
"v": "No Drive Detected",
"vt": "str"
},
{
"t": "else"
}
],
2021-12-03 02:34:14 +01:00
"checkall": "false",
"repair": false,
"outputs": 2,
"x": 520,
"y": 900,
"wires": [
[
2021-12-03 02:34:14 +01:00
"9a577caa.1535a"
],
[
"c74fe76.bcba718",
"4997446.5176d3c"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "9a577caa.1535a",
"type": "ui_toast",
"z": "1371dec5.76e671",
"position": "dialog",
"displayTime": "5",
"highlight": "",
"sendall": true,
"outputs": 1,
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
"name": "",
2021-12-03 02:34:14 +01:00
"x": 710,
"y": 880,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "72f5e8d4.40fcb8",
"type": "ui_toast",
"z": "1371dec5.76e671",
"position": "top right",
"displayTime": "5",
"highlight": "",
"sendall": true,
"outputs": 0,
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
"name": "",
2021-12-03 02:34:14 +01:00
"x": 930,
"y": 900,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "81a48376.27a66",
"type": "ui_toast",
"z": "1371dec5.76e671",
"position": "dialog",
"displayTime": "5",
"highlight": "",
"sendall": true,
"outputs": 1,
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
"name": "",
"x": 1110,
"y": 980,
"wires": [
[]
]
},
{
"id": "a2c36500.6d376",
"type": "function",
"z": "1371dec5.76e671",
"name": "return code",
"func": "switch (msg.payload.code){\ncase 0:\n msg.payload = \"All files successfully copied, you can remove your usb drive!\";\n break;\ncase 1:\n msg.payload = \"There was an error!\";\n break;\ncase 2:\n msg.payload = \"Error: your device is already mounted\"\n break;\ncase 3:\n msg.payload = \"Error while mounting the device\"\n break;\ncase 4:\n msg.payload = \"Error: some files were corrupted during the copy!\"\n break;\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 910,
"y": 940,
"wires": [
[
2021-12-03 02:34:14 +01:00
"81a48376.27a66"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "dde0025c.3df9f",
"type": "ui_button",
"z": "1371dec5.76e671",
"name": "Purge local",
"group": "52d1b77.28369c8",
"order": 4,
"width": 0,
"height": 0,
"passthru": false,
"label": "Purge local data",
"tooltip": "",
"color": "",
"bgcolor": "#AD1625",
"icon": "delete_forever",
"payload": "THIS IS GOING TO WIPE ALL THE LOCAL DATA. MAKE SURE YOU DID A BACKUP TO AN USB DRIVE BEFOREHAND.",
"payloadType": "str",
"topic": "BE EXTRA CAREFUL",
"x": 150,
"y": 1020,
"wires": [
[
"ba72704a.8a38a"
]
]
},
{
"id": "ba72704a.8a38a",
"type": "ui_toast",
"z": "1371dec5.76e671",
"position": "dialog",
"displayTime": "5",
"highlight": "",
"sendall": true,
"outputs": 1,
"ok": "GO BACK",
"cancel": "YES I'M SURE, I WANT TO PURGE THE DATA",
"raw": false,
"topic": "Data purge",
"name": "",
"x": 330,
"y": 1020,
"wires": [
[
2021-12-03 02:34:14 +01:00
"d80e6307.3aaf8"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d80e6307.3aaf8",
"type": "switch",
"z": "1371dec5.76e671",
"name": "",
2021-12-03 02:34:14 +01:00
"property": "payload",
"propertyType": "msg",
"rules": [
{
2021-12-03 02:34:14 +01:00
"t": "cont",
"v": "YES I'M SURE",
"vt": "str"
}
],
2021-12-03 02:34:14 +01:00
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 510,
"y": 1020,
"wires": [
2021-12-03 02:34:14 +01:00
[
"4a8cb5d4.52e4bc"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4a8cb5d4.52e4bc",
"type": "exec",
"z": "1371dec5.76e671",
"command": "rm -rf /home/pi/data/img /home/pi/data/objects /home/pi/data/export",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "rm data",
"x": 700,
"y": 1020,
"wires": [
[
2021-12-03 02:34:14 +01:00
"c5b1e763.d1bba8"
],
[],
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "c5b1e763.d1bba8",
"type": "function",
"z": "1371dec5.76e671",
"name": "error message",
"func": "// TODO revoir cette partie\n\nswitch (msg.rc.code){\ncase 0:\n msg.payload = \"/home/pi/data was purged successfully!\";\n break;\ndefault:\n msg.payload = \"There was an error! The error is \" + msg.rc.message;\n break;\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 900,
"y": 1020,
"wires": [
[
2021-12-03 02:34:14 +01:00
"81a48376.27a66"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6d09f428.930bcc",
"type": "delay",
"z": "1371dec5.76e671",
"name": "",
2021-12-03 02:34:14 +01:00
"pauseType": "rate",
"timeout": "5",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "5",
"rateUnits": "minute",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": true,
"outputs": 1,
"x": 760,
"y": 420,
"wires": [
2021-12-03 02:34:14 +01:00
[
"24df077a.cb252"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4997446.5176d3c",
"type": "change",
2021-12-03 02:34:14 +01:00
"z": "1371dec5.76e671",
"name": "Backup starting message",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
2021-12-03 02:34:14 +01:00
"to": "\"The backup is starting now, please wait for the complete message before removing the drive\"",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
2021-12-03 02:34:14 +01:00
"x": 750,
"y": 980,
"wires": [
[
2021-12-03 02:34:14 +01:00
"81a48376.27a66"
]
]
2021-09-06 18:39:52 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "6c7f0cc5.ea30d4",
"type": "gpsd",
2021-09-06 18:39:52 +02:00
"z": "1371dec5.76e671",
2021-12-03 02:34:14 +01:00
"name": "",
"hostname": "localhost",
"port": "2947",
"tpv": true,
"sky": false,
"info": false,
"device": false,
"gst": false,
"att": false,
"x": 170,
"y": 1360,
2021-09-06 18:39:52 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"906072b1.42c7c8",
"cd3b05d6.5a3e2",
"9b29e0e2.475e08",
"96fefa8b.b7d538",
"49a98fe9.9450d8",
"f2a62d0.f5e135"
2021-09-06 18:39:52 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "96bc0179.5c37",
"type": "ui_text",
"z": "1371dec5.76e671",
"group": "cfe2288f.a8862",
"order": 1,
"width": 6,
"height": 1,
"name": "GPS Status Display",
"label": "GPS Status:",
"format": "{{msg.payload}}",
"layout": "row-center",
"x": 660,
"y": 1200,
"wires": []
},
{
"id": "8c731f52.2b5f3",
"type": "ui_text",
"z": "1371dec5.76e671",
"group": "cfe2288f.a8862",
"order": 5,
"width": 3,
"height": 1,
"name": "Latitude",
"label": "Latitude",
"format": "{{msg.payload.lat.deg}}&deg{{msg.payload.lat.min}}'{{msg.payload.lat.sec}}{{msg.payload.lat.dir}}",
"layout": "col-center",
"x": 620,
"y": 1240,
2021-09-06 18:39:52 +02:00
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "34e4b5ea.27b4a2",
"type": "ui_text",
"z": "1371dec5.76e671",
"group": "cfe2288f.a8862",
"order": 6,
"width": 3,
"height": 1,
"name": "Longitude",
"label": "Longitude",
"format": "{{msg.payload.lon.deg}}&deg{{msg.payload.lon.min}}'{{msg.payload.lon.sec}}{{msg.payload.lon.dir}}",
"layout": "col-center",
"x": 620,
"y": 1280,
2021-09-06 18:39:52 +02:00
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "906072b1.42c7c8",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "1371dec5.76e671",
"name": "Convert DD to DMS",
"func": "function ConvertDDToDMS(D, lng){\n // from https://stackoverflow.com/a/5786281/2108279\n return {\n dir : D<0?lng?'W':'S':lng?'E':'N',\n deg : 0|(D<0?D=-D:D),\n min : 0|D%1*60,\n sec :(0|D*60%1*6000)/100\n };\n}\n\nmsg.payload = {\n \"lat\":ConvertDDToDMS(msg.payload.lat, false),\n \"lon\":ConvertDDToDMS(msg.payload.lon, true)\n};\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 420,
"y": 1260,
"wires": [
[
2021-12-03 02:34:14 +01:00
"8c731f52.2b5f3",
"34e4b5ea.27b4a2"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4584c817.0be238",
"type": "ui_text",
"z": "1371dec5.76e671",
"group": "cfe2288f.a8862",
"order": 3,
"width": 3,
"height": 1,
"name": "Speed",
"label": "Speed",
"format": "{{msg.payload}} kts",
"layout": "col-center",
"x": 610,
"y": 1320,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "cd3b05d6.5a3e2",
"type": "ui_text",
"z": "1371dec5.76e671",
"group": "cfe2288f.a8862",
"order": 4,
"width": 3,
"height": 1,
"name": "Direction",
"label": "Direction",
"format": "{{msg.payload.track}} °",
"layout": "col-center",
"x": 620,
"y": 1360,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "9b29e0e2.475e08",
"type": "function",
"z": "1371dec5.76e671",
"name": "GPS Mode",
"func": "switch (msg.payload.mode){\n case 1:msg.payload = \"No Fix\"; break\n case 2:msg.payload = \"2D Fix\"; break\n case 3:msg.payload = \"3D Fix\"; break\n default: msg.payload = \"No info\"\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 390,
"y": 1200,
"wires": [
[
2021-12-03 02:34:14 +01:00
"96bc0179.5c37"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "96fefa8b.b7d538",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "1371dec5.76e671",
"name": "Speed conversion",
"func": "msg.payload = (0|msg.payload.speed) * 1.9438\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 410,
"y": 1320,
"wires": [
[
2021-12-03 02:34:14 +01:00
"4584c817.0be238"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "49a98fe9.9450d8",
"type": "ui_text",
"z": "1371dec5.76e671",
"group": "cfe2288f.a8862",
"order": 2,
"width": 6,
"height": 1,
"name": "GPS UTC datetime",
"label": "GPS UTC datetime",
"format": "{{msg.payload.time}}",
"layout": "col-center",
"x": 650,
"y": 1400,
"wires": []
},
{
"id": "3c3f334e.d691ac",
"type": "ui_button",
"z": "1371dec5.76e671",
"name": "",
"group": "cfe2288f.a8862",
"order": 6,
"width": 0,
"height": 0,
"passthru": false,
"label": "Force local clock update",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "",
"payload": "",
"payloadType": "str",
"topic": "topic",
"x": 670,
"y": 1480,
"wires": [
[
2021-12-03 02:34:14 +01:00
"c60030d4.317418"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "c60030d4.317418",
"type": "exec",
"z": "1371dec5.76e671",
"command": "sudo hwclock -w",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "",
"x": 920,
"y": 1480,
"wires": [
2021-12-03 02:34:14 +01:00
[],
[],
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6d144078.508d9",
"type": "ui_text",
"z": "1371dec5.76e671",
"group": "cfe2288f.a8862",
"order": 2,
"width": 6,
"height": 1,
"name": "Local UTC datetime",
"label": "Local UTC datetime",
"format": "{{msg.payload}}",
"layout": "col-center",
"x": 660,
"y": 1440,
"wires": []
},
{
"id": "f2a62d0.f5e135",
"type": "function",
"z": "1371dec5.76e671",
"name": "",
2021-12-03 02:34:14 +01:00
"func": "msg.payload = new Date()\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 380,
"y": 1440,
"wires": [
[
2021-12-03 02:34:14 +01:00
"6d144078.508d9"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "9373b3f6.33a2b",
"type": "link in",
"z": "1371dec5.76e671",
"name": "Git Rev",
"links": [
"6c20d6a7.869b6"
],
2021-12-03 02:34:14 +01:00
"x": 375,
"y": 520,
"wires": [
[
2021-12-03 02:34:14 +01:00
"3e64877a.9684b"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "e640ebb5c397f424",
"type": "delay",
"z": "1371dec5.76e671",
"name": "",
"pauseType": "delay",
"timeout": "1",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"allowrate": false,
"outputs": 1,
"x": 1300,
"y": 40,
"wires": [
[
2021-12-03 02:34:14 +01:00
"b7ab1ada.1f4158"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "7fb13a4a53b612a0",
"type": "rbe",
"z": "1371dec5.76e671",
"name": "",
2021-12-03 02:34:14 +01:00
"func": "rbe",
"gap": "",
"start": "",
"inout": "out",
"septopics": true,
"property": "payload",
"topi": "topic",
"x": 1010,
"y": 40,
"wires": [
2021-12-03 02:34:14 +01:00
[
"2549f778.4eb828"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "b9bf06d6.26c518",
"type": "exec",
"z": "f21ba04.c26266",
"command": "sudo iwlist wlan0 scan | grep ESSID | sed 's/ESSID://g;s/\"//g;s/^ *//;s/ *$//'",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "scan",
"x": 450,
"y": 80,
"wires": [
[
2021-12-03 02:34:14 +01:00
"9642cb31.663b4"
],
[],
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "9642cb31.663b4",
"type": "function",
"z": "f21ba04.c26266",
"name": "parseOptions",
"func": "var ssids = msg.payload.split('\\n').filter(s => !!s)\n\nssids = [...new Set(ssids)];\n\nmsg.options = ssids\nmsg.payload = null\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
2021-12-03 02:51:25 +01:00
"x": 610,
2021-12-03 02:34:14 +01:00
"y": 80,
"wires": [
[
2021-12-03 02:34:14 +01:00
"dbc0d44.5361b28"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "dbc0d44.5361b28",
"type": "ui_dropdown",
"z": "f21ba04.c26266",
"name": "",
"label": "Wifi",
"tooltip": "",
"place": "Select a WIFI",
"group": "9e409235.73cd7",
"order": 1,
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"passthru": false,
"multiple": false,
"options": [],
"payload": "",
"topic": "",
"x": 770,
"y": 80,
"wires": [
[
2021-12-03 02:34:14 +01:00
"cdfa9013.b91278"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "2bf338e3.966468",
"type": "ui_button",
"z": "f21ba04.c26266",
"name": "",
"group": "9e409235.73cd7",
"order": 2,
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"passthru": false,
"label": "Scan",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "fa-wifi fa-2x",
"payload": "true",
"payloadType": "bool",
"topic": "",
"x": 270,
"y": 40,
"wires": [
[
2021-12-03 02:34:14 +01:00
"b9bf06d6.26c518"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4c0400c.69a1e8",
"type": "exec",
"z": "f21ba04.c26266",
"command": "ip -j addr show",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "getInfo",
2021-12-03 02:51:25 +01:00
"x": 450,
2021-12-03 02:34:14 +01:00
"y": 180,
"wires": [
[
"88e05f37a8745216"
],
[],
[]
]
},
{
"id": "bcb20e39.00cea8",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "f21ba04.c26266",
"name": "parse IP Info",
"func": "var ip = \"\";\nvar broadcast = \"\";\nvar iface = \"\";\nvar iface_up = false;\n\nif (msg.rc.code === 0){\n payload = msg.payload\n for (let info in payload) {\n if (payload[info].operstate === \"UP\") {\n \tif (iface_up){\n \t// We have two concurrent interfaces up\n // We need to deactivate one\n ip = \"Remove Ethernet cable or disconnect wifi\"\n broadcast = \"Remove Ethernet cable or disconnect wifi\"\n iface = \"Remove Ethernet cable or disconnect wifi\"\n break\n }\n if (payload[info].ifname === \"eth0\") {\n iface = \"Ethernet\"\n } else if (payload[info].ifname === \"wifi0\" || payload[info].ifname === \"wlan0\") {\n iface = \"Wifi\"\n }\n ip = payload[info].addr_info[0].local;\n broadcast = payload[info].addr_info[0].broadcast;\n iface_up = true;\n }\n }\n}\n\nif (iface_up) {\n return([{\n topic: 'interface',\n payload: iface\n }, {\n topic: 'ip',\n payload: ip\n }, {\n topic: 'broadcast',\n payload: broadcast\n }])\n}\n",
"outputs": 3,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"libs": [],
"x": 730,
"y": 180,
"wires": [
[
2021-12-03 02:34:14 +01:00
"67d052ba.99ff24"
],
[
"d71c8185.800fd",
"49111a72f4e32740"
],
[
"dc305a26.b33fc"
]
2021-12-03 02:34:14 +01:00
],
"outputLabels": [
"interface",
"ip",
"broadcast"
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d71c8185.800fd",
"type": "ui_text",
"z": "f21ba04.c26266",
"group": "919923a3.d10868",
"order": 2,
"width": 0,
"height": 0,
"name": "",
"label": "IP",
"format": "{{msg.payload || '---'}}",
"layout": "row-spread",
"x": 930,
"y": 180,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "67d052ba.99ff24",
"type": "ui_text",
"z": "f21ba04.c26266",
"group": "919923a3.d10868",
"order": 1,
"width": 0,
"height": 0,
"name": "",
"label": "Interface",
"format": "{{msg.payload || '---'}}",
"layout": "row-spread",
"x": 940,
"y": 140,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "dc305a26.b33fc",
"type": "ui_text",
"z": "f21ba04.c26266",
"group": "919923a3.d10868",
"order": 3,
"width": 0,
"height": 0,
"name": "",
"label": "Broadcast",
"format": "{{msg.payload || '---'}}",
"layout": "row-spread",
2021-12-03 02:51:25 +01:00
"x": 940,
2021-12-03 02:34:14 +01:00
"y": 220,
"wires": []
},
{
"id": "94b2e8fc.aa20e",
"type": "ui_form",
"z": "f21ba04.c26266",
"name": "",
"label": "Update",
"group": "9e409235.73cd7",
"order": 3,
"width": 0,
"height": 0,
"options": [
{
2021-12-03 02:34:14 +01:00
"label": "SSID",
"value": "ssid",
"type": "text",
"required": true,
"rows": null
},
{
2021-12-03 02:34:14 +01:00
"label": "Password",
"value": "password",
"type": "text",
"required": true,
"rows": null
}
],
2021-12-03 02:34:14 +01:00
"formValue": {
"ssid": "",
"password": ""
},
"payload": "",
"submit": "Add the network",
"cancel": "Reset fields",
"topic": "",
"x": 1100,
"y": 80,
"wires": [
[
2021-12-03 02:34:14 +01:00
"d59021e3.1b63e8"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "cdfa9013.b91278",
"type": "function",
"z": "f21ba04.c26266",
"name": "insert ssid",
"func": "\nmsg.payload = {ssid: msg.payload}\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 930,
"y": 80,
"wires": [
[
2021-12-03 02:34:14 +01:00
"94b2e8fc.aa20e"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "448b4c37.be50bc",
"type": "exec",
"z": "f21ba04.c26266",
"command": "iw dev wlan0 info",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "getCurrentSSID",
"x": 480,
"y": 260,
"wires": [
[
"151819d4.0dbe56"
],
[],
[]
]
},
{
"id": "c8a5ae48.ef1308",
"type": "ui_text",
"z": "f21ba04.c26266",
"group": "919923a3.d10868",
"order": 4,
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"name": "",
"label": "SSID",
"format": "{{msg.payload || '---'}}",
"layout": "row-spread",
"x": 930,
"y": 259,
"wires": []
},
{
"id": "151819d4.0dbe56",
"type": "function",
"z": "f21ba04.c26266",
"name": "parse SSID",
"func": "var ssid = \"\"\n\nif (msg.rc.code === 0){\n ssid = msg.payload.match(/\\sssid (.+)/)[1];\n return({topic: 'ssid', payload: ssid})\n}\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 730,
"y": 260,
"wires": [
[
2021-12-03 02:34:14 +01:00
"c8a5ae48.ef1308"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "ff059f3f.9c0f28",
"type": "ui_ui_control",
"z": "f21ba04.c26266",
"name": "onTab",
"events": "all",
"x": 130,
"y": 80,
"wires": [
[
2021-12-03 02:34:14 +01:00
"f103cd20eb4362a4"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "167d7bd0.679ce4",
"type": "function",
"z": "f21ba04.c26266",
"name": "update contry code",
"func": "var template = `sudo sed -i \\\"s/^country.*/country=\"${msg.payload}\"/\\\" /home/pi/wpa_supplicant.conf`\n// sudo tee /etc/wpa_supplicant/wpa_supplicant.conf <<EOF\n//ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\n//update_config=1\n//country=IT\nmsg.payload = template\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 630,
"y": 400,
"wires": [
[
2021-12-03 02:34:14 +01:00
"9d30f7b5.aac88"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "9d30f7b5.aac88",
"type": "exec",
"z": "f21ba04.c26266",
"command": "",
"addpay": true,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "updateConf",
"x": 890,
"y": 400,
"wires": [
[],
[],
[]
]
},
{
"id": "deb8c14e.ff1fd",
"type": "ui_button",
"z": "f21ba04.c26266",
"name": "",
"group": "9e409235.73cd7",
"order": 5,
"width": 3,
"height": 1,
2021-12-03 02:34:14 +01:00
"passthru": false,
"label": "Change Country Code",
"tooltip": "",
"color": "",
"bgcolor": "",
"icon": "",
"payload": "country_code",
"payloadType": "flow",
"topic": "",
"x": 400,
"y": 400,
"wires": [
[
2021-12-03 02:34:14 +01:00
"167d7bd0.679ce4"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6d4ce391.04abf4",
"type": "ui_text_input",
2021-12-03 02:34:14 +01:00
"z": "f21ba04.c26266",
"name": "country_code",
"label": "Country Code",
"tooltip": "ISO/IEC alpha2 country code",
"group": "9e409235.73cd7",
"order": 4,
"width": 3,
"height": 1,
"passthru": true,
2021-12-03 02:34:14 +01:00
"mode": "text",
"delay": "500",
"topic": "country_code",
"x": 420,
"y": 360,
"wires": [
[
"3f60469d.620aea"
]
]
},
{
"id": "3f60469d.620aea",
"type": "change",
"z": "f21ba04.c26266",
"name": "",
"rules": [
{
"t": "set",
"p": "country_code",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 640,
"y": 360,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "d59021e3.1b63e8",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "f21ba04.c26266",
"name": "add network",
"func": "var data = msg.payload\n\nvar command = `wpa_passphrase \"${data.ssid}\" \"${data.password}\" | sed '/#psk=\".*\"/d' | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf > /dev/null`\n \nmsg.payload = command\n\nreturn msg",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:34:14 +01:00
"x": 1270,
"y": 80,
"wires": [
[
2021-12-03 02:34:14 +01:00
"64a1f2f2.3de8b4"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "64a1f2f2.3de8b4",
"type": "exec",
"z": "f21ba04.c26266",
"command": "",
"addpay": true,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "Save Network",
"x": 1460,
"y": 80,
"wires": [
2021-12-03 02:34:14 +01:00
[
"6868b4c4.65987c"
],
[],
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "6868b4c4.65987c",
"type": "exec",
"z": "f21ba04.c26266",
"command": "sudo /usr/bin/autohotspotN >/dev/null",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "reconfigure",
"x": 1250,
"y": 180,
"wires": [
[
2021-12-03 02:34:14 +01:00
"59e24c40.e1a4dc"
],
[],
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "59e24c40.e1a4dc",
"type": "function",
"z": "f21ba04.c26266",
"name": "showMessage",
"func": "msg.payload = \"Wifi configuration updated successfully\"\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 1420,
"y": 180,
"wires": [
[
2021-12-03 02:34:14 +01:00
"4ecb6be2.ad39f4"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4ecb6be2.ad39f4",
"type": "ui_toast",
"z": "f21ba04.c26266",
"position": "dialog",
"displayTime": "3",
"highlight": "",
"sendall": true,
"outputs": 1,
2021-12-03 02:34:14 +01:00
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "",
"name": "",
"x": 1590,
"y": 180,
"wires": [
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "991d1df9.3ffb3",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "f21ba04.c26266",
"name": "reset wpa_supplicant.conf",
"func": "country_code = flow.get(\"country_code\")\n\nvar template = `sudo tee /etc/wpa_supplicant/wpa_supplicant.conf <<EOF\nctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\ncountry=${country_code}\n\nEOF`\n\nmsg.payload = template\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
2021-12-03 02:51:25 +01:00
"x": 650,
2021-12-03 02:34:14 +01:00
"y": 460,
"wires": [
[
2021-12-03 02:34:14 +01:00
"2124997d.2cbf0e"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "2124997d.2cbf0e",
"type": "exec",
"z": "f21ba04.c26266",
"command": "",
"addpay": true,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "resetConf",
"x": 880,
"y": 460,
"wires": [
2021-12-03 02:34:14 +01:00
[],
[],
[]
]
2021-09-06 18:54:34 +02:00
},
{
2021-12-03 02:34:14 +01:00
"id": "8b3a1ecb.c4d2e8",
"type": "ui_button",
"z": "f21ba04.c26266",
"name": "",
"group": "9e409235.73cd7",
"order": 6,
2021-09-06 18:54:34 +02:00
"width": 0,
"height": 0,
"passthru": false,
2021-12-03 02:34:14 +01:00
"label": "Reset wifi networks",
"tooltip": "",
"color": "",
"bgcolor": "#AD1625",
"icon": "",
"payload": "country_code",
"payloadType": "flow",
"topic": "",
"x": 410,
"y": 460,
"wires": [
[
"991d1df9.3ffb3"
]
]
},
{
"id": "49111a72f4e32740",
"type": "link out",
"z": "f21ba04.c26266",
"name": "IP",
"links": [
"142bc61a0bde1ba9",
"c515edcd8681c244",
"7fb91a7fd6d2518c",
"604659d148759b84",
"3bb4970164f5e7b2"
],
"x": 1035,
"y": 160,
"wires": []
},
{
"id": "3db572cd.cf972e",
"type": "inject",
"z": "f21ba04.c26266",
"name": "once",
"props": [
2021-09-06 18:54:34 +02:00
{
2021-12-03 02:34:14 +01:00
"p": "payload"
2021-09-06 18:54:34 +02:00
},
{
2021-12-03 02:34:14 +01:00
"p": "topic",
"vt": "str"
2021-09-06 18:54:34 +02:00
}
],
2021-12-03 02:34:14 +01:00
"repeat": "60",
"crontab": "",
"once": true,
"onceDelay": "1",
"topic": "",
"payloadType": "date",
"x": 270,
"y": 180,
2021-09-06 18:54:34 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"b9bf06d6.26c518",
"4c0400c.69a1e8",
"448b4c37.be50bc"
2021-09-06 18:54:34 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "88e05f37a8745216",
"type": "json",
"z": "f21ba04.c26266",
2021-09-06 18:54:34 +02:00
"name": "",
2021-12-03 02:34:14 +01:00
"property": "payload",
"action": "obj",
"pretty": false,
"x": 590,
"y": 180,
2021-09-06 18:54:34 +02:00
"wires": [
2021-12-03 02:34:14 +01:00
[
"bcb20e39.00cea8"
]
2021-09-06 18:54:34 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "f103cd20eb4362a4",
2021-09-06 18:54:34 +02:00
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "f21ba04.c26266",
"name": "check tab",
"func": "if (msg.name === \"Wifi\"){\n return [msg, msg]\n}\n\nif ([\"Optic Configuration\", \"Fluidic Acquisition\", \"Segmentation\", \"Gallery\"].includes(msg.name)){\n return [null, msg]\n}",
"outputs": 2,
2021-09-06 18:54:34 +02:00
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
2021-12-03 02:34:14 +01:00
"x": 260,
"y": 80,
2021-09-06 18:54:34 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"b9bf06d6.26c518",
"4c0400c.69a1e8",
"448b4c37.be50bc"
2021-09-06 18:54:34 +02:00
],
[
2021-12-03 02:34:14 +01:00
"4c0400c.69a1e8",
"448b4c37.be50bc"
2021-09-06 18:54:34 +02:00
]
2021-12-03 02:34:14 +01:00
],
"outputLabels": [
"interface",
"ip"
2021-09-06 18:54:34 +02:00
]
},
{
2021-12-03 02:34:14 +01:00
"id": "1fdf77b5.24e4e",
"type": "mqtt in",
"z": "9a22e67a.378818",
2021-09-06 18:54:34 +02:00
"name": "",
2021-12-03 02:34:14 +01:00
"topic": "status/#",
"qos": "0",
"datatype": "json",
"broker": "8dc3722c.06efa8",
"inputs": 0,
"x": 290,
"y": 340,
2021-09-06 18:54:34 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"fa73983d.318188"
2021-09-06 18:54:34 +02:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "4a1e9e3e.27506",
"type": "switch",
"z": "9a22e67a.378818",
"name": "Filter segmenter out",
"property": "topic",
"propertyType": "msg",
"rules": [
{
"t": "else"
},
{
"t": "cont",
"v": "status/segmenter",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 2,
"x": 620,
"y": 340,
2021-09-06 18:54:34 +02:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"58f2e0f.4e8b12"
],
[
2021-12-03 02:34:14 +01:00
"dcf5bd45.16a8d"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "fa73983d.318188",
"type": "json",
"z": "9a22e67a.378818",
"name": "",
"property": "payload",
"action": "obj",
"pretty": true,
"x": 430,
"y": 340,
"wires": [
[
2021-12-03 02:34:14 +01:00
"4a1e9e3e.27506"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "58f2e0f.4e8b12",
"type": "link out",
"z": "9a22e67a.378818",
"name": "Fluidic module status",
"links": [
"bb628f8d.98f108"
],
"x": 795,
2021-12-01 13:30:23 +01:00
"y": 320,
2021-12-03 02:34:14 +01:00
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "dcf5bd45.16a8d",
"type": "link out",
"z": "9a22e67a.378818",
"name": "Segmenter module status",
"links": [
"25867454.a8e334"
],
"x": 795,
"y": 360,
"wires": []
},
{
2021-12-03 02:34:14 +01:00
"id": "44efaeb3.59bf6",
"type": "subflow:4ed26b8b.253504",
"z": "1eaf21c8.f7a21e",
"name": "",
"env": [],
"x": 120,
"y": 440,
"wires": [
[
2021-12-03 02:34:14 +01:00
"e10f5e55.00b828",
"c534fd26.13741",
"54ba7f16.709ad8",
"c67c305004f87e39",
"11955bbeefc29ab4"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "e10f5e55.00b828",
"type": "change",
"z": "1eaf21c8.f7a21e",
2021-12-03 02:34:14 +01:00
"name": "Get stepper_type",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
2021-12-03 02:34:14 +01:00
"to": "payload.stepper_type",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
2021-12-03 02:34:14 +01:00
"x": 350,
"y": 360,
"wires": [
[
2021-12-03 02:34:14 +01:00
"dee52a36.2af72"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "dee52a36.2af72",
"type": "ui_dropdown",
"z": "1eaf21c8.f7a21e",
2021-12-03 02:34:14 +01:00
"name": "stepper_type",
"label": "",
"tooltip": "",
2021-12-03 02:34:14 +01:00
"place": "",
"group": "6be36295.0ab324",
2021-12-03 02:34:14 +01:00
"order": 4,
"width": 3,
"height": 1,
"passthru": false,
"multiple": false,
"options": [
{
2021-12-03 02:34:14 +01:00
"label": "",
"value": "adafruit",
"type": "str"
},
{
2021-12-03 02:34:14 +01:00
"label": "",
"value": "waveshare",
"type": "str"
},
{
2021-12-03 02:34:14 +01:00
"label": "",
"value": "pscope_hat",
"type": "str"
}
],
"payload": "",
2021-12-03 02:34:14 +01:00
"topic": "stepper_type",
"topicType": "str",
2021-12-03 02:34:14 +01:00
"x": 670,
"y": 360,
"wires": [
[
2021-12-03 02:34:14 +01:00
"2068e7f.f4efb18",
"8e3b3d3c.955148"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "e41870d7.300eb8",
"type": "link out",
"z": "1eaf21c8.f7a21e",
2021-12-03 02:34:14 +01:00
"name": "Stepper config change",
"links": [
"45a7b5aa.2ed20c"
],
2021-12-03 02:34:14 +01:00
"x": 1195,
"y": 380,
"wires": []
},
{
"id": "2068e7f.f4efb18",
"type": "delay",
"z": "1eaf21c8.f7a21e",
"name": "",
"pauseType": "delay",
"timeout": "1",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"outputs": 1,
"x": 880,
"y": 380,
"wires": [
[
2021-12-03 02:34:14 +01:00
"e41870d7.300eb8"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "8e3b3d3c.955148",
"type": "subflow:4ed26b8b.253504",
"z": "1eaf21c8.f7a21e",
"name": "",
"env": [],
"x": 900,
"y": 420,
"wires": [
[]
]
},
{
"id": "c534fd26.13741",
"type": "change",
"z": "1eaf21c8.f7a21e",
2021-12-03 02:34:14 +01:00
"name": "Get stepper_reverse",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
2021-12-03 02:34:14 +01:00
"to": "payload.stepper_reverse",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
2021-12-03 02:34:14 +01:00
"x": 360,
"y": 440,
"wires": [
[
2021-12-03 02:34:14 +01:00
"cd1987c7.027f58"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "cd1987c7.027f58",
"type": "ui_switch",
"z": "1eaf21c8.f7a21e",
"name": "stepper_reverse",
"label": "Invert stepper output",
"tooltip": "Stepper 1 is controlled by output 1 or the other way around",
"group": "6be36295.0ab324",
"order": 5,
"width": 0,
"height": 0,
2021-12-03 02:34:14 +01:00
"passthru": false,
"decouple": "false",
"topic": "stepper_reverse",
"style": "",
"onvalue": "true",
"onvalueType": "bool",
"onicon": "",
"oncolor": "",
"offvalue": "false",
"offvalueType": "bool",
"officon": "",
"offcolor": "",
"x": 660,
"y": 440,
"wires": [
2021-12-03 02:34:14 +01:00
[
"8e3b3d3c.955148",
"2068e7f.f4efb18"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "54ba7f16.709ad8",
"type": "change",
"z": "1eaf21c8.f7a21e",
"name": "Get pump_steps_per_ml",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload.pump_steps_per_ml",
"tot": "msg"
}
],
2021-12-03 02:34:14 +01:00
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 370,
"y": 400,
"wires": [
[
2021-12-03 02:34:14 +01:00
"ee58b91c.396108"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "ee58b91c.396108",
"type": "ui_text_input",
"z": "1eaf21c8.f7a21e",
"name": "pump_steps_per_ml",
"label": "Pump: steps per mL",
"tooltip": "",
"group": "6be36295.0ab324",
"order": 6,
"width": 0,
"height": 0,
"passthru": false,
"mode": "number",
"delay": "2000",
"topic": "pump_steps_per_ml",
"topicType": "str",
"x": 640,
"y": 400,
"wires": [
[
2021-12-03 02:34:14 +01:00
"2068e7f.f4efb18",
"8e3b3d3c.955148"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "be888117.5cd67",
"type": "ui_template",
"z": "1eaf21c8.f7a21e",
"group": "6be36295.0ab324",
"name": "Stepper controller type header",
"order": 3,
"width": 3,
"height": 1,
"format": "<div>Stepper controller type</div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"x": 550,
"y": 100,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "7534dfd9.8cf3e8",
"type": "ui_template",
"z": "1eaf21c8.f7a21e",
"group": "6be36295.0ab324",
"name": "Information",
"order": 2,
"width": 6,
"height": 3,
"format": "<div style=\"text-align: center;\">\n <p>Changing one of these values will make the python back-end to restart.</p>\n <p><strong style=\"font-size: large;\">If you are not sure, you probably should not be touching anything here!</strong></p>\n</div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"x": 610,
"y": 60,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "a7e5c7f4.644678",
"type": "ui_dropdown",
"z": "1eaf21c8.f7a21e",
"name": "Machine Version",
"label": "Machine version",
"tooltip": "",
"place": "Select option",
"group": "6be36295.0ab324",
"order": 1,
"width": 0,
"height": 0,
"passthru": false,
"multiple": false,
"options": [
{
"label": "",
"value": "PlanktoScope v1.0",
"type": "str"
},
{
"label": "PlanktoScope 2.1 (adafruit version)",
"value": "PlanktoScope v2.1",
"type": "str"
},
{
"label": "PlanktoScope 2.2 (waveshare hat)",
"value": "PlanktoScope v2.2",
"type": "str"
},
{
"label": "PlanktoScope 2.3 (custom hat)",
"value": "PlanktoScope v2.3",
"type": "str"
}
],
2021-12-03 02:34:14 +01:00
"payload": "",
"topic": "acq_instrument",
"topicType": "str",
2021-12-03 02:51:25 +01:00
"x": 600,
2021-12-03 02:34:14 +01:00
"y": 160,
"wires": [
[
2021-12-03 02:34:14 +01:00
"3e2c5c1c.4c57b4"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "1cfadc66.3cde8c",
"type": "subflow:1c24ad9c.bebec2",
"z": "1eaf21c8.f7a21e",
"name": "",
"env": [],
"x": 690,
"y": 280,
"wires": [
2021-12-03 02:34:14 +01:00
[]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "3e2c5c1c.4c57b4",
"type": "function",
"z": "1eaf21c8.f7a21e",
"name": "Set and update settings",
"func": "global.set(msg.topic,msg.payload);\n\nif (msg.payload.includes(\"2.1\")){\n msg_stepper_type = {topic:\"stepper_type\", payload:\"adafruit\"};\n msg_pump_steps = {topic:\"pump_steps_per_ml\", payload:1600};\n return [msg, msg_stepper_type, msg_pump_steps];\n}\nelse if (msg.payload.includes(\"2.2\")){\n msg_stepper_type = {topic:\"stepper_type\", payload:\"waveshare\"};\n msg_pump_steps = {topic:\"pump_steps_per_ml\", payload:2000};\n return [msg, msg_stepper_type, msg_pump_steps];\n}\nelse if (msg.payload.includes(\"2.3\")){\n msg_stepper_type = {topic:\"stepper_type\", payload:\"pscope_hat\"};\n msg_pump_steps = {topic:\"pump_steps_per_ml\", payload:3200};\n return [msg, msg_stepper_type, msg_pump_steps];\n}\nelse{\n return [msg, null, null];\n}",
"outputs": 3,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 370,
"y": 280,
"wires": [
[
2021-12-03 02:34:14 +01:00
"1cfadc66.3cde8c"
],
[
"dee52a36.2af72",
"8e3b3d3c.955148",
"2068e7f.f4efb18"
],
[
"ee58b91c.396108",
"8e3b3d3c.955148"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "427c312.fc5e65",
"type": "subflow:1c24ad9c.bebec2",
"z": "1eaf21c8.f7a21e",
"name": "",
"env": [],
"x": 150,
"y": 160,
"wires": [
2021-12-03 02:34:14 +01:00
[
"237809c1.2c64ce"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "237809c1.2c64ce",
"type": "function",
"z": "1eaf21c8.f7a21e",
"name": "get acq_instrument",
"func": "msg.payload = msg.payload.acq_instrument;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 350,
"y": 160,
"wires": [
[
2021-12-03 02:34:14 +01:00
"a7e5c7f4.644678"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "c67c305004f87e39",
"type": "change",
"z": "1eaf21c8.f7a21e",
"name": "Get process_pixel_fixed",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload.process_pixel_fixed",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 370,
"y": 480,
"wires": [
[
2021-12-03 02:34:14 +01:00
"244ca5dd62df2bcd"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "244ca5dd62df2bcd",
"type": "ui_text_input",
2021-12-03 02:34:14 +01:00
"z": "1eaf21c8.f7a21e",
"name": "process_pixel_fixed",
"label": "Pixel size calibration",
"tooltip": "Object size in µm / pixel. 0 will remove this calibration.",
"group": "6be36295.0ab324",
"order": 8,
2021-12-03 02:34:14 +01:00
"width": 0,
"height": 0,
"passthru": true,
"mode": "number",
2021-12-03 02:34:14 +01:00
"delay": "1000",
"topic": "process_pixel_fixed",
"topicType": "str",
2021-12-03 02:51:25 +01:00
"x": 650,
2021-12-03 02:34:14 +01:00
"y": 480,
"wires": [
[
2021-12-03 02:34:14 +01:00
"8e3b3d3c.955148",
"f9b47f4b1da15778"
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "f9b47f4b1da15778",
"type": "function",
2021-12-03 02:34:14 +01:00
"z": "1eaf21c8.f7a21e",
"name": "Set or remove if null",
"func": "if (msg.payload == 0){\n global.set(msg.topic,undefined)\n}\nelse {\n global.set(msg.topic,msg.payload);\n}\n\nreturn msg",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
2021-12-03 02:51:25 +01:00
"x": 920,
2021-12-03 02:34:14 +01:00
"y": 480,
"wires": [
[
2021-12-03 02:34:14 +01:00
"5d5ad36d2c50dcc2"
]
2021-12-03 02:34:14 +01:00
]
},
{
"id": "5d5ad36d2c50dcc2",
"type": "link out",
"z": "1eaf21c8.f7a21e",
"name": "Process_pixel calibration",
"links": [
"cfc783d7.d6ceb"
],
2021-12-03 02:34:14 +01:00
"x": 1195,
"y": 500,
"wires": []
},
{
"id": "6146ba22df928516",
"type": "ui_dropdown",
"z": "1eaf21c8.f7a21e",
"name": "acq_fnumber_objective",
"label": "M12 Lens",
"tooltip": "",
"place": "Select option",
"group": "6be36295.0ab324",
"order": 7,
"width": 0,
"height": 0,
"passthru": true,
"multiple": false,
"options": [
{
"label": "f 25mm 1/2\" 5MP IR",
"value": 25,
"type": "num"
},
{
"label": "f 16mm 1/2.5\" 5MP IR",
"value": 16,
"type": "num"
},
{
"label": "f 12mm 1/2.5\" 5MP IR",
"value": 12,
"type": "num"
},
{
"label": "f 8mm 1/2.5\" 5MP IR",
"value": 8,
"type": "num"
},
{
"label": "f 6mm 1/2.5\" 5MP IR",
"value": 6,
"type": "num"
}
],
"payload": "",
"topic": "acq_fnumber_objective",
"topicType": "str",
"x": 630,
"y": 520,
"wires": [
[
"a6983d3232b570a7",
"8e3b3d3c.955148"
]
]
2021-12-01 13:09:42 +01:00
},
{
2021-12-03 02:34:14 +01:00
"id": "a6983d3232b570a7",
"type": "change",
"z": "1eaf21c8.f7a21e",
2021-12-01 13:09:42 +01:00
"name": "",
2021-12-03 02:34:14 +01:00
"rules": [
{
"t": "set",
"p": "acq_fnumber_objective",
"pt": "global",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 960,
"y": 520,
2021-12-01 13:09:42 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"5d5ad36d2c50dcc2"
2021-12-01 13:09:42 +01:00
]
]
},
{
2021-12-03 02:34:14 +01:00
"id": "11955bbeefc29ab4",
"type": "change",
"z": "1eaf21c8.f7a21e",
"name": "Get acq_fnumber_objective",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload.acq_fnumber_objective",
"tot": "msg"
},
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "acq_fnumber_objective",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 380,
"y": 520,
2021-12-01 13:09:42 +01:00
"wires": [
[
2021-12-03 02:34:14 +01:00
"6146ba22df928516"
2021-12-01 13:09:42 +01:00
]
]
2020-01-08 14:32:46 +01:00
}
2020-11-25 16:58:32 +01:00
]