From 0b5b48ede4651e21c58d337dec9466d5ea2387bb Mon Sep 17 00:00:00 2001 From: rschoene <rene.schoene@tu-dresden.de> Date: Thu, 6 May 2021 16:24:55 +0200 Subject: [PATCH] integrate protobuf messages, add auto-scroll log. --- README.md | 9 + cgv_connector.proto | 75 ++++ cgv_connector_pb2.py | 601 ++++++++++++++++++++++++++ config/config-scene-a.json | 20 + config/config-scene-b.json | 21 + config/dummy-reachability-b-arm1.json | 11 + config/dummy-reachability-b-arm2.json | 11 + main.py | 235 ++++++++-- requirements.txt | 2 + 9 files changed, 956 insertions(+), 29 deletions(-) create mode 100644 README.md create mode 100644 cgv_connector.proto create mode 100644 cgv_connector_pb2.py create mode 100644 config/config-scene-a.json create mode 100644 config/config-scene-b.json create mode 100644 config/dummy-reachability-b-arm1.json create mode 100644 config/dummy-reachability-b-arm2.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..6e8e463 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Dashboard for ros3rag use case + +[Java impl repo](https://git-st.inf.tu-dresden.de/jastadd/ros3rag) | [Paper repo](https://git-st.inf.tu-dresden.de/stgroup/publications/2021/ragconnect-followup) | [Paper issue](https://git-st.inf.tu-dresden.de/stgroup/publications/kanban/2021/-/issues/3) + +## Running + +- Install requirements, best using virtual environment +- Run `python main.py` +- Open <http://127.0.0.1:8050/> diff --git a/cgv_connector.proto b/cgv_connector.proto new file mode 100644 index 0000000..be68b97 --- /dev/null +++ b/cgv_connector.proto @@ -0,0 +1,75 @@ +// cgv_connector.proto +// this file contains the messages that are exchanged between the cgv framework and the st ROS interface + +syntax = "proto3"; + +option java_package = "de.tudresden.inf.st.ceti"; +option java_multiple_files = true; + +message Object { + + // Position is object-center related + message Position { + float x = 1; // in m + float y = 2; // in m + float z = 3; // height in m + } + + // 3D description of the object + message Size { + float length = 1; // in m + float width = 2; // in m + float height = 3; // in m + } + message Orientation { + float x = 1; // normalized quaternion + float y = 2; + float z = 3; + float w = 4; + } + message Color { + float r = 1; // 0..1 + float g = 2; // 0..1 + float b = 3; // 0..1 + } + enum Type { + UNKNOWN = 0; + BOX = 1; + BIN = 2; + ARM = 3; + } + + string id = 1; + Type type = 2; + Position pos = 3; + Size size = 4; + Orientation orientation = 5; + Color color = 6; +} + +// the scene is stored within the ROS side and sent to the CGV framework +message Scene { + repeated Object objects = 1; +} + +// the selection is done by the CGV framework and sent to ROS +message Selection { + string id = 1; // the id corresponds to an id of an Object in a Scene +} + +// vvv from rs vvv. +// Merged message to contain both pick and place in one +message MergedSelection { + string idRobot = 1; // the id corresponds to and id of the robot Object that should execute this operation + string idPick = 2; // the id corresponds to an id of the Object in a Scene to be picked + string idPlace = 3; // the id corresponds to an id of the Object in a Scene where the picked object shall be placed +} +// Reachability of objects, as reported by MoveIt +message Reachability { + message ObjectReachability { + string idObject = 1; // the id of the object to reach + bool reachable = 2; // whether the object can be reached + } + string idRobot = 1; // the id of the robot arm + repeated ObjectReachability objects = 2; // all objects reachable +} diff --git a/cgv_connector_pb2.py b/cgv_connector_pb2.py new file mode 100644 index 0000000..9323422 --- /dev/null +++ b/cgv_connector_pb2.py @@ -0,0 +1,601 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: cgv_connector.proto + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='cgv_connector.proto', + package='', + syntax='proto3', + serialized_options=b'\n\030de.tudresden.inf.st.cetiP\001', + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x13\x63gv_connector.proto\"\xac\x03\n\x06Object\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1a\n\x04type\x18\x02 \x01(\x0e\x32\x0c.Object.Type\x12\x1d\n\x03pos\x18\x03 \x01(\x0b\x32\x10.Object.Position\x12\x1a\n\x04size\x18\x04 \x01(\x0b\x32\x0c.Object.Size\x12(\n\x0borientation\x18\x05 \x01(\x0b\x32\x13.Object.Orientation\x12\x1c\n\x05\x63olor\x18\x06 \x01(\x0b\x32\r.Object.Color\x1a+\n\x08Position\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\x1a\x35\n\x04Size\x12\x0e\n\x06length\x18\x01 \x01(\x02\x12\r\n\x05width\x18\x02 \x01(\x02\x12\x0e\n\x06height\x18\x03 \x01(\x02\x1a\x39\n\x0bOrientation\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\x12\t\n\x01w\x18\x04 \x01(\x02\x1a(\n\x05\x43olor\x12\t\n\x01r\x18\x01 \x01(\x02\x12\t\n\x01g\x18\x02 \x01(\x02\x12\t\n\x01\x62\x18\x03 \x01(\x02\".\n\x04Type\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x07\n\x03\x42OX\x10\x01\x12\x07\n\x03\x42IN\x10\x02\x12\x07\n\x03\x41RM\x10\x03\"!\n\x05Scene\x12\x18\n\x07objects\x18\x01 \x03(\x0b\x32\x07.Object\"\x17\n\tSelection\x12\n\n\x02id\x18\x01 \x01(\t\"C\n\x0fMergedSelection\x12\x0f\n\x07idRobot\x18\x01 \x01(\t\x12\x0e\n\x06idPick\x18\x02 \x01(\t\x12\x0f\n\x07idPlace\x18\x03 \x01(\t\"\x8d\x01\n\x0cReachability\x12\x0f\n\x07idRobot\x18\x01 \x01(\t\x12\x31\n\x07objects\x18\x02 \x03(\x0b\x32 .Reachability.ObjectReachability\x1a\x39\n\x12ObjectReachability\x12\x10\n\x08idObject\x18\x01 \x01(\t\x12\x11\n\treachable\x18\x02 \x01(\x08\x42\x1c\n\x18\x64\x65.tudresden.inf.st.cetiP\x01\x62\x06proto3' +) + + + +_OBJECT_TYPE = _descriptor.EnumDescriptor( + name='Type', + full_name='Object.Type', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='BOX', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='BIN', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='ARM', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=406, + serialized_end=452, +) +_sym_db.RegisterEnumDescriptor(_OBJECT_TYPE) + + +_OBJECT_POSITION = _descriptor.Descriptor( + name='Position', + full_name='Object.Position', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='x', full_name='Object.Position.x', index=0, + number=1, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='y', full_name='Object.Position.y', index=1, + number=2, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='z', full_name='Object.Position.z', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=205, + serialized_end=248, +) + +_OBJECT_SIZE = _descriptor.Descriptor( + name='Size', + full_name='Object.Size', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='length', full_name='Object.Size.length', index=0, + number=1, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='width', full_name='Object.Size.width', index=1, + number=2, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='height', full_name='Object.Size.height', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=250, + serialized_end=303, +) + +_OBJECT_ORIENTATION = _descriptor.Descriptor( + name='Orientation', + full_name='Object.Orientation', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='x', full_name='Object.Orientation.x', index=0, + number=1, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='y', full_name='Object.Orientation.y', index=1, + number=2, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='z', full_name='Object.Orientation.z', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='w', full_name='Object.Orientation.w', index=3, + number=4, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=305, + serialized_end=362, +) + +_OBJECT_COLOR = _descriptor.Descriptor( + name='Color', + full_name='Object.Color', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='r', full_name='Object.Color.r', index=0, + number=1, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='g', full_name='Object.Color.g', index=1, + number=2, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='b', full_name='Object.Color.b', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=364, + serialized_end=404, +) + +_OBJECT = _descriptor.Descriptor( + name='Object', + full_name='Object', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='Object.id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type', full_name='Object.type', index=1, + number=2, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='pos', full_name='Object.pos', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='size', full_name='Object.size', index=3, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='orientation', full_name='Object.orientation', index=4, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='color', full_name='Object.color', index=5, + number=6, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_OBJECT_POSITION, _OBJECT_SIZE, _OBJECT_ORIENTATION, _OBJECT_COLOR, ], + enum_types=[ + _OBJECT_TYPE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=24, + serialized_end=452, +) + + +_SCENE = _descriptor.Descriptor( + name='Scene', + full_name='Scene', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='objects', full_name='Scene.objects', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=454, + serialized_end=487, +) + + +_SELECTION = _descriptor.Descriptor( + name='Selection', + full_name='Selection', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='Selection.id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=489, + serialized_end=512, +) + + +_MERGEDSELECTION = _descriptor.Descriptor( + name='MergedSelection', + full_name='MergedSelection', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='idRobot', full_name='MergedSelection.idRobot', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='idPick', full_name='MergedSelection.idPick', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='idPlace', full_name='MergedSelection.idPlace', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=514, + serialized_end=581, +) + + +_REACHABILITY_OBJECTREACHABILITY = _descriptor.Descriptor( + name='ObjectReachability', + full_name='Reachability.ObjectReachability', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='idObject', full_name='Reachability.ObjectReachability.idObject', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reachable', full_name='Reachability.ObjectReachability.reachable', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=668, + serialized_end=725, +) + +_REACHABILITY = _descriptor.Descriptor( + name='Reachability', + full_name='Reachability', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='idRobot', full_name='Reachability.idRobot', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='objects', full_name='Reachability.objects', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_REACHABILITY_OBJECTREACHABILITY, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=584, + serialized_end=725, +) + +_OBJECT_POSITION.containing_type = _OBJECT +_OBJECT_SIZE.containing_type = _OBJECT +_OBJECT_ORIENTATION.containing_type = _OBJECT +_OBJECT_COLOR.containing_type = _OBJECT +_OBJECT.fields_by_name['type'].enum_type = _OBJECT_TYPE +_OBJECT.fields_by_name['pos'].message_type = _OBJECT_POSITION +_OBJECT.fields_by_name['size'].message_type = _OBJECT_SIZE +_OBJECT.fields_by_name['orientation'].message_type = _OBJECT_ORIENTATION +_OBJECT.fields_by_name['color'].message_type = _OBJECT_COLOR +_OBJECT_TYPE.containing_type = _OBJECT +_SCENE.fields_by_name['objects'].message_type = _OBJECT +_REACHABILITY_OBJECTREACHABILITY.containing_type = _REACHABILITY +_REACHABILITY.fields_by_name['objects'].message_type = _REACHABILITY_OBJECTREACHABILITY +DESCRIPTOR.message_types_by_name['Object'] = _OBJECT +DESCRIPTOR.message_types_by_name['Scene'] = _SCENE +DESCRIPTOR.message_types_by_name['Selection'] = _SELECTION +DESCRIPTOR.message_types_by_name['MergedSelection'] = _MERGEDSELECTION +DESCRIPTOR.message_types_by_name['Reachability'] = _REACHABILITY +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Object = _reflection.GeneratedProtocolMessageType('Object', (_message.Message,), { + + 'Position' : _reflection.GeneratedProtocolMessageType('Position', (_message.Message,), { + 'DESCRIPTOR' : _OBJECT_POSITION, + '__module__' : 'cgv_connector_pb2' + # @@protoc_insertion_point(class_scope:Object.Position) + }) + , + + 'Size' : _reflection.GeneratedProtocolMessageType('Size', (_message.Message,), { + 'DESCRIPTOR' : _OBJECT_SIZE, + '__module__' : 'cgv_connector_pb2' + # @@protoc_insertion_point(class_scope:Object.Size) + }) + , + + 'Orientation' : _reflection.GeneratedProtocolMessageType('Orientation', (_message.Message,), { + 'DESCRIPTOR' : _OBJECT_ORIENTATION, + '__module__' : 'cgv_connector_pb2' + # @@protoc_insertion_point(class_scope:Object.Orientation) + }) + , + + 'Color' : _reflection.GeneratedProtocolMessageType('Color', (_message.Message,), { + 'DESCRIPTOR' : _OBJECT_COLOR, + '__module__' : 'cgv_connector_pb2' + # @@protoc_insertion_point(class_scope:Object.Color) + }) + , + 'DESCRIPTOR' : _OBJECT, + '__module__' : 'cgv_connector_pb2' + # @@protoc_insertion_point(class_scope:Object) + }) +_sym_db.RegisterMessage(Object) +_sym_db.RegisterMessage(Object.Position) +_sym_db.RegisterMessage(Object.Size) +_sym_db.RegisterMessage(Object.Orientation) +_sym_db.RegisterMessage(Object.Color) + +Scene = _reflection.GeneratedProtocolMessageType('Scene', (_message.Message,), { + 'DESCRIPTOR' : _SCENE, + '__module__' : 'cgv_connector_pb2' + # @@protoc_insertion_point(class_scope:Scene) + }) +_sym_db.RegisterMessage(Scene) + +Selection = _reflection.GeneratedProtocolMessageType('Selection', (_message.Message,), { + 'DESCRIPTOR' : _SELECTION, + '__module__' : 'cgv_connector_pb2' + # @@protoc_insertion_point(class_scope:Selection) + }) +_sym_db.RegisterMessage(Selection) + +MergedSelection = _reflection.GeneratedProtocolMessageType('MergedSelection', (_message.Message,), { + 'DESCRIPTOR' : _MERGEDSELECTION, + '__module__' : 'cgv_connector_pb2' + # @@protoc_insertion_point(class_scope:MergedSelection) + }) +_sym_db.RegisterMessage(MergedSelection) + +Reachability = _reflection.GeneratedProtocolMessageType('Reachability', (_message.Message,), { + + 'ObjectReachability' : _reflection.GeneratedProtocolMessageType('ObjectReachability', (_message.Message,), { + 'DESCRIPTOR' : _REACHABILITY_OBJECTREACHABILITY, + '__module__' : 'cgv_connector_pb2' + # @@protoc_insertion_point(class_scope:Reachability.ObjectReachability) + }) + , + 'DESCRIPTOR' : _REACHABILITY, + '__module__' : 'cgv_connector_pb2' + # @@protoc_insertion_point(class_scope:Reachability) + }) +_sym_db.RegisterMessage(Reachability) +_sym_db.RegisterMessage(Reachability.ObjectReachability) + + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/config/config-scene-a.json b/config/config-scene-a.json new file mode 100644 index 0000000..9f09d08 --- /dev/null +++ b/config/config-scene-a.json @@ -0,0 +1,20 @@ +{ "objects": [ + { "id": "tablePillar1","pos": { "x": 0.77,"y": 0.77,"z": 0.325 },"size": { "length": 0.06,"width": 0.06,"height": 0.65 },"orientation": { "w": 1 },"color": { "r": 255,"g": 222,"b": 173 } }, + { "id": "tablePillar2","pos": { "x": -0.77,"y": -0.77,"z": 0.325 },"size": { "length": 0.06,"width": 0.06,"height": 0.65 },"orientation": { "w": 1 },"color": { "r": 255,"g": 222,"b": 173 } }, + { "id": "tablePillar3","pos": { "x": -0.77,"y": 0.77,"z": 0.325 },"size": { "length": 0.06,"width": 0.06,"height": 0.65 },"orientation": { "w": 1 },"color": { "r": 255,"g": 222,"b": 173 } }, + { "id": "tablePillar4","pos": { "x": 0.77,"y": -0.77,"z": 0.325 },"size": { "length": 0.06,"width": 0.06,"height": 0.65 },"orientation": { "w": 1 },"color": { "r": 255,"g": 222,"b": 173 } }, + { "id": "table","pos": { "z": 0.7 },"size": { "length": 1.6,"width": 1.6,"height": 0.1 },"orientation": { "w": 1 },"color": { "r": 255,"g": 222,"b": 173 } }, + { "id": "binBlue","type": "BIN","pos": { "x": -0.34,"y": 0.49,"z": 0.8325 },"size": { "length": 0.21,"width": 0.3,"height": 0.165 },"orientation": { "w": 1 },"color": { "b": 1 } }, + { "id": "binRed","type": "BIN","pos": { "x": 0.06,"y": 0.49,"z": 0.8325 },"size": { "length": 0.21,"width": 0.3,"height": 0.165 },"orientation": { "w": 1 },"color": { "r": 1 } }, + { "id": "binGreen","type": "BIN","pos": { "x": 0.46,"y": 0.49,"z": 0.8325 },"size": { "length": 0.21,"width": 0.3,"height": 0.165 },"orientation": { "w": 1 },"color": { "g": 1 } }, + { "id": "objectRed1","type": "BOX","pos": { "x": 0.5,"y": -0.1,"z": 0.8105 },"size": { "length": 0.031,"width": 0.062,"height": 0.121 },"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "r": 1 } }, + { "id": "objectRed2","type": "BOX","pos": { "x": 0.25,"y": -0.2,"z": 0.8105 },"size": { "length": 0.031,"width": 0.062,"height": 0.121 },"orientation": { "w": 1 },"color": { "r": 1 } }, + { "id": "objectRed3","type": "BOX","pos": { "x": -0.4,"z": 0.819 },"size": { "length": 0.031,"width": 0.031,"height": 0.138 },"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "r": 1 } }, + { "id": "objectGreen1","type": "BOX","pos": { "x": 0.45,"y": -0.3,"z": 0.8105 },"size": {"length":0.031,"width":0.062,"height":0.121},"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "g": 1 } }, + { "id": "objectGreen2","type": "BOX","pos": { "x": -0.45,"y": -0.2,"z": 0.8105 },"size": {"length":0.031,"width":0.031,"height":0.138},"orientation": { "z": 1,"w": 0.92388 },"color": { "g": 1 } }, + { "id": "objectGreen3","type": "BOX","pos": { "x": 0.1,"y": -0.3,"z": 0.819 },"size": {"length":0.031,"width":0.062,"height":0.121},"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "g": 1 } }, + { "id": "objectBlue1","type": "BOX","pos": { "x": 0.25,"y": -0.4,"z": 0.8105 },"size": { "length": 0.031,"width": 0.062,"height": 0.121 },"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "b": 1 } }, + { "id": "objectBlue2","type": "BOX","pos": { "x": -0.3,"y": -0.3,"z": 0.8105 },"size": { "length": 0.031,"width": 0.062,"height": 0.121 },"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "b": 1 } }, + { "id": "objectBlue3","type": "BOX","pos": { "x": 0.4,"z": 0.819 },"size": { "length": 0.031,"width": 0.031,"height": 0.138 },"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "b": 1 } }, + { "id": "arm","type": "ARM","pos": { "z": 0.75 },"size": { },"orientation": { "w": 1 },"color": { "r": 1.00,"g": 1.00,"b": 1.00 } } +] } diff --git a/config/config-scene-b.json b/config/config-scene-b.json new file mode 100644 index 0000000..f14e17f --- /dev/null +++ b/config/config-scene-b.json @@ -0,0 +1,21 @@ +{ "objects": [ + { "id": "tablePillar1","pos": { "x": 0.77,"y": 0.77,"z": 0.325 },"size": { "length": 0.06,"width": 0.06,"height": 0.65 },"orientation": { "w": 1 },"color": { "r": 255,"g": 222,"b": 173 } }, + { "id": "tablePillar2","pos": { "x": -0.77,"y": -0.77,"z": 0.325 },"size": { "length": 0.06,"width": 0.06,"height": 0.65 },"orientation": { "w": 1 },"color": { "r": 255,"g": 222,"b": 173 } }, + { "id": "tablePillar3","pos": { "x": -0.77,"y": 0.77,"z": 0.325 },"size": { "length": 0.06,"width": 0.06,"height": 0.65 },"orientation": { "w": 1 },"color": { "r": 255,"g": 222,"b": 173 } }, + { "id": "tablePillar4","pos": { "x": 0.77,"y": -0.77,"z": 0.325 },"size": { "length": 0.06,"width": 0.06,"height": 0.65 },"orientation": { "w": 1 },"color": { "r": 255,"g": 222,"b": 173 } }, + { "id": "table","pos": { "z": 0.7 },"size": { "length": 1.6,"width": 1.6,"height": 0.1 },"orientation": { "w": 1 },"color": { "r": 255,"g": 222,"b": 173 } }, + { "id": "binBlue","type": "BIN","pos": { "x": -0.34,"y": 0.49,"z": 0.8325 },"size": { "length": 0.21,"width": 0.3,"height": 0.165 },"orientation": { "w": 1 },"color": { "b": 1 } }, + { "id": "binRed","type": "BIN","pos": { "x": 0.06,"y": 0.49,"z": 0.8325 },"size": { "length": 0.21,"width": 0.3,"height": 0.165 },"orientation": { "w": 1 },"color": { "r": 1 } }, + { "id": "binGreen","type": "BIN","pos": { "x": 0.46,"y": 0.49,"z": 0.8325 },"size": { "length": 0.21,"width": 0.3,"height": 0.165 },"orientation": { "w": 1 },"color": { "g": 1 } }, + { "id": "objectRed1","type": "BOX","pos": { "x": 0.5,"y": -0.1,"z": 0.8105 },"size": { "length": 0.031,"width": 0.062,"height": 0.121 },"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "r": 1 } }, + { "id": "objectRed2","type": "BOX","pos": { "x": 0.25,"y": -0.2,"z": 0.8105 },"size": { "length": 0.031,"width": 0.062,"height": 0.121 },"orientation": { "w": 1 },"color": { "r": 1 } }, + { "id": "objectRed3","type": "BOX","pos": { "x": -0.4,"z": 0.819 },"size": { "length": 0.031,"width": 0.031,"height": 0.138 },"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "r": 1 } }, + { "id": "objectGreen1","type": "BOX","pos": { "x": 0.45,"y": -0.3,"z": 0.8105 },"size": {"length":0.031,"width":0.062,"height":0.121},"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "g": 1 } }, + { "id": "objectGreen2","type": "BOX","pos": { "x": -0.45,"y": -0.2,"z": 0.8105 },"size": {"length":0.031,"width":0.031,"height":0.138},"orientation": { "z": 1,"w": 0.92388 },"color": { "g": 1 } }, + { "id": "objectGreen3","type": "BOX","pos": { "x": 0.1,"y": -0.3,"z": 0.819 },"size": {"length":0.031,"width":0.062,"height":0.121},"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "g": 1 } }, + { "id": "objectBlue1","type": "BOX","pos": { "x": 0.25,"y": -0.4,"z": 0.8105 },"size": { "length": 0.031,"width": 0.062,"height": 0.121 },"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "b": 1 } }, + { "id": "objectBlue2","type": "BOX","pos": { "x": -0.3,"y": -0.3,"z": 0.8105 },"size": { "length": 0.031,"width": 0.062,"height": 0.121 },"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "b": 1 } }, + { "id": "objectBlue3","type": "BOX","pos": { "x": 0.4,"z": 0.819 },"size": { "length": 0.031,"width": 0.031,"height": 0.138 },"orientation": { "z": 0.382683,"w": 0.92388 },"color": { "b": 1 } }, + { "id": "arm1","type": "ARM","pos": { "z": 0.75 },"size": { },"orientation": { "w": 1 },"color": { "r": 1.00,"g": 1.00,"b": 1.00 } }, + { "id": "arm2","type": "ARM","pos": { "x": 1, "z": 0.75 },"size": { },"orientation": { "w": 1 },"color": { "r": 1.00,"g": 1.00,"b": 1.00 } } +] } diff --git a/config/dummy-reachability-b-arm1.json b/config/dummy-reachability-b-arm1.json new file mode 100644 index 0000000..d238077 --- /dev/null +++ b/config/dummy-reachability-b-arm1.json @@ -0,0 +1,11 @@ +{ + "idRobot": "ARM1", + "objects": [ + { "idObject": "objectRed1", "reachable": true }, + { "idObject": "objectRed2", "reachable": true }, + { "idObject": "objectRed3", "reachable": false }, + { "idObject": "binRed", "reachable": true }, + { "idObject": "binBlue", "reachable": true }, + { "idObject": "binGreen", "reachable": false } + ] +} diff --git a/config/dummy-reachability-b-arm2.json b/config/dummy-reachability-b-arm2.json new file mode 100644 index 0000000..c058487 --- /dev/null +++ b/config/dummy-reachability-b-arm2.json @@ -0,0 +1,11 @@ +{ + "idRobot": "ARM2", + "objects": [ + { "idObject": "objectRed1", "reachable": false }, + { "idObject": "objectRed2", "reachable": false }, + { "idObject": "objectRed3", "reachable": false }, + { "idObject": "binRed", "reachable": false }, + { "idObject": "binBlue", "reachable": true }, + { "idObject": "binGreen", "reachable": true } + ] +} diff --git a/main.py b/main.py index 4bcbf54..bbedf27 100644 --- a/main.py +++ b/main.py @@ -8,8 +8,11 @@ import dash import dash_core_components as dcc import dash_html_components as html import paho.mqtt.client as mqtt +import visdcc from dash.dependencies import Input, Output, State +from google.protobuf import json_format +import cgv_connector_pb2 import utils external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] @@ -39,34 +42,77 @@ commands = { 'send-place-b-demo-objRed-red': ('place-b/demo/move/objectRed1/red', '1'), } +# button-id: (topic, textarea-content-id, protobuf-object) +complex_commands = { + 'send-place-a-update': + ('place-a/scene/update', 'place-a-input-json', cgv_connector_pb2.Scene()), + 'send-place-b-update': + ('place-b/scene/update', 'place-b-input-json', cgv_connector_pb2.Scene()), + 'send-place-b-reachability-1-update': + ('place-b/reachability/arm1', 'place-b-reachability-1-json', cgv_connector_pb2.Reachability()), + 'send-place-b-reachability-2-update': + ('place-b/reachability/arm2', 'place-b-reachability-2-json', cgv_connector_pb2.Reachability()), +} + +bytes_topics = [ + 'place-a/scene/update', + 'place-b/scene/update', + 'place-b/reachability/arm1', + 'place-b/reachability/arm2', + 'place-b/commands', +] + button_style_normal = {"marginRight": "15px"} button_style_exit = {**button_style_normal, "backgroundColor": "red", "color": "white"} +textarea_style_normal = {'width': '100%', 'height': '200px', 'resize': 'vertical'} app = dash.Dash(__name__, external_stylesheets=external_stylesheets) app.layout = html.Div([ - html.Div([ - html.Div([ + html.Div([ # First Row + html.Div([ # Column for place a dcc.Textarea( id='place-a-input-json', placeholder='place-a-input-json', value='{}', - style={'width': '100%'} + style=textarea_style_normal ), - html.Button('Send to place-a/scene/update', id='send-place-a-update', disabled=True), + html.Button('Send to place-a/scene/update', id='send-place-a-update'), + html.Div(id='hidden-div-scene-a', style={'display': 'none'}) ], className="six columns"), - html.Div([ + html.Div([ # Column for place b dcc.Textarea( id='place-b-input-json', placeholder='place-b-input-json', value='{}', - style={'width': '100%'} + style=textarea_style_normal ), - html.Button('Send to place-b/scene/update', id='send-place-b-update', disabled=True), + html.Button('Send to place-b/scene/update', id='send-place-b-update'), + html.Div(id='hidden-div-scene-b', style={'display': 'none'}), + html.Div([ # Row for reachability + html.Div([ # Column for reachability arm 1 + dcc.Textarea( + id='place-b-reachability-1-json', + placeholder='place-b-reachability-1-json', + value='{}', + style=textarea_style_normal + ), + html.Button('Send to place-b/reachability/arm1', id='send-place-b-reachability-1-update'), + ], className="six columns"), + html.Div([ # Column for reachability arm 2 + dcc.Textarea( + id='place-b-reachability-2-json', + placeholder='place-b-reachability-2-json', + value='{}', + style=textarea_style_normal + ), + html.Button('Send to place-b/reachability/arm2', id='send-place-b-reachability-2-update'), + ], className="six columns"), + ], className='row', style={'marginTop': '15px'}), ], className="six columns"), ], className='row'), - dcc.Markdown("---"), - html.Div([ - html.Div([ + # dcc.Markdown("---"), + html.Div([ # Row for commands + html.Div([ # Column for commands of place a html.H3("Commands Place A"), html.Button('Model', id='send-place-a-model', style=button_style_normal), html.Button('Model (Details)', id='send-place-a-model-details', style=button_style_normal), @@ -74,7 +120,7 @@ app.layout = html.Div([ html.Button('obj-Red -> Red', id='send-place-a-demo-objRed-red', style=button_style_normal), html.Button('obj-Red -> Blue', id='send-place-a-demo-objRed-blue', style=button_style_normal), ], className="six columns"), - html.Div([ + html.Div([ # Column for commands of place b html.H3("Commands Place B"), html.Button('Model', id='send-place-b-model', style=button_style_normal), html.Button('Model (Details)', id='send-place-b-model-details', style=button_style_normal), @@ -82,42 +128,146 @@ app.layout = html.Div([ html.Button('obj-Red -> Red', id='send-place-b-demo-objRed-red', style=button_style_normal), ], className="six columns"), ], className='row'), - dcc.Markdown("---"), + # dcc.Markdown("---"), html.H3("MQTT Log"), dcc.Textarea( id='mqtt-log', value="", readOnly=True, - style={'width': '100%', 'height': '200px', 'fontFamily': 'Consolas, monospace'} + style={**textarea_style_normal, 'fontFamily': 'Consolas, monospace'} + ), + dcc.Checklist( + id="should-scroll-mqtt-log", + options=[{"label": "Auto-Scroll", "value": "Auto-Scroll"}], + value=["Auto-Scroll"], + labelStyle={"display": "inline-block"}, ), dcc.Markdown("---"), html.Div([ - # html.Div([ - html.P("Topic"), - dcc.Input(id='manual-mqtt-topic'), - html.P("Message"), - dcc.Input(id='manual-mqtt-message'), - # dcc.Textarea(id='manual-mqtt-message', style={'width': '50%', 'font-family': 'Consolas, monospace'}), - html.Button('Send', id='manual-mqtt-send') - # ], className="six columns"), - ], className='row'), + html.P("Topic"), + dcc.Input(id='manual-mqtt-topic'), + html.P("Message"), + dcc.Input(id='manual-mqtt-message'), + # dcc.Textarea(id='manual-mqtt-message', style={'width': '50%', 'font-family': 'Consolas, monospace'}), + html.Button('Send', id='manual-mqtt-send') + ], className='row', style=dict(display='flex')), # -- Invisible elements -- + dcc.Location(id='url', refresh=False), dcc.Interval( id='every-1-second', interval=1000, # in milliseconds n_intervals=0 ), + visdcc.Run_js(id='javascriptLog', run=""), html.Div(id='hidden-div', style={'display': 'none'}) ]) +@app.callback( + Output('place-a-input-json', 'value'), + Output('place-b-input-json', 'value'), + Output('place-b-reachability-1-json', 'value'), + Output('place-b-reachability-2-json', 'value'), + Input('url', 'pathname') +) +def set_initial_json_content(_pathname): + """ + Set initial JSON content from files in config/ directory + :param _pathname: Unused value of page URL + :return: list of contents + """ + with open('config/config-scene-a.json') as fdr: + json_content_a = fdr.read() + with open('config/config-scene-b.json') as fdr: + json_content_b = fdr.read() + with open('config/dummy-reachability-b-arm1.json') as fdr: + json_content_b_reachability_1 = fdr.read() + with open('config/dummy-reachability-b-arm2.json') as fdr: + json_content_b_reachability_2 = fdr.read() + return json_content_a, json_content_b, json_content_b_reachability_1, json_content_b_reachability_2 + + +def send_json_for_object_to_topic(json_content: str, obj, topic: str): + """ + Use JSON to populate protobuf object and send its serialization to the given topic + :param json_content: Content to initialize the object represented in JSON + :param obj: The object to send + :param topic: The MQTT topic to send the serialization + :return: None + """ + json_format.Parse(json_content, obj) + mqttc.publish(topic=topic, payload=obj.SerializeToString()) + + +# @app.callback( +# Output('hidden-div-scene-a', 'children'), +# Input('send-place-a-update', 'n_clicks'), +# State('place-a-input-json', 'value') +# ) +# def send_scene_a(n_clicks, json_content): +# """ +# Send scene in place a +# :param n_clicks: button.clicks +# :param json_content: state of place-a-input-json +# :return: no_update +# """ +# if n_clicks: +# scene = cgv_connector_pb2.Scene() +# send_json_for_object_to_topic(json_content, scene, 'place-a/scene/update') +# return dash.no_update +# +# +# @app.callback( +# Output('hidden-div-scene-b', 'children'), +# Input('send-place-b-update', 'n_clicks'), +# State('place-b-input-json', 'value') +# ) +# def send_scene_a(n_clicks, json_content): +# """ +# Send scene in place b +# :param n_clicks: button.clicks +# :param json_content:state of place-b-input-json +# :return:no_update +# """ +# if n_clicks: +# scene = cgv_connector_pb2.Scene() +# send_json_for_object_to_topic(json_content, scene, 'place-b/scene/update') +# return dash.no_update +@app.callback( + Output('hidden-div-scene-b', 'children'), + [Input(button_id, 'n_clicks') for button_id in complex_commands], + [State(value[1], 'value') for value in complex_commands.values()] +) +def send_complex(*_): + ctx = dash.callback_context + + if not ctx.triggered: + button_id = None + else: + button_id = ctx.triggered[0]['prop_id'].split('.')[0] + if button_id: + topic, state_id, protobuf_obj = complex_commands[button_id] + json_content = ctx.states[state_id + ".value"] + send_json_for_object_to_topic(json_content, protobuf_obj, topic) + return dash.no_update + + @app.callback( Output('mqtt-log', 'value'), + Output('javascriptLog', 'run'), Input('every-1-second', 'n_intervals'), State('mqtt-log', 'value'), + State('should-scroll-mqtt-log', 'value') ) -def append_to_mqtt_log(_, value): +def append_to_mqtt_log(_n_intervals, value, should_scroll): + """ + Periodically update mqtt log + :param _n_intervals: Unused value of intervals + :param value: current content of mqtt log + :param should_scroll: checkbox value whether to scroll to the end after update + :return: new content of mqtt log + """ local_messages = [] while not message_queue.empty(): local_messages.append(message_queue.get_nowait()) @@ -136,7 +286,13 @@ def append_to_mqtt_log(_, value): # reformatted_lines.append(utils.format_log_msg(topic, max_topic.max_mqtt_topic_length, # message, timestamp=timestamp)) # value = '\n'.join(reformatted_lines) - return value + else: + return dash.no_update + log_cmd = ''' + var textarea = document.getElementById('mqtt-log'); + textarea.scrollTop = textarea.scrollHeight; + ''' if should_scroll else "" + return value, log_cmd @app.callback( @@ -144,6 +300,11 @@ def append_to_mqtt_log(_, value): [Input(button_id, 'n_clicks') for button_id in commands] ) def button_clicked_to_add_to_mqtt_log(*_): + """ + Based on dict "commands", send message to topic + :param _: Unused "catch-all" variable for n_clicks of all buttons + :return: no_update + """ ctx = dash.callback_context if not ctx.triggered: @@ -163,28 +324,44 @@ def button_clicked_to_add_to_mqtt_log(*_): State('manual-mqtt-message', 'value'), ) def send_manual(n_clicks, topic, message): + """ + Manual sending of mqtt message + :param n_clicks: button.n_clicks + :param topic: value of input field manual-mqtt-topic + :param message: value of input field manual-mqtt-message + :return: no_update + """ if n_clicks: mqttc.publish(topic=topic, payload=message) return dash.no_update def on_mqtt_connect(_client, _userdata, _flags, _rc, _properties=None): + # Callback for mqtt client when connected print('Connected at ' + datetime.datetime.now().isoformat()) ready_event.set() def on_mqtt_message(_client, _userdata, message): + # Callback for mqtt client when message was received max_mqtt_topic_length = max_topic.process_topic(message.topic) - try: - payload = message.payload.decode("utf-8") - except UnicodeDecodeError: - payload = "(unreadable bytes)" + if message.topic in bytes_topics: + payload = "(ignored bytes)" + else: + try: + payload = message.payload.decode("utf-8") + except UnicodeDecodeError: + payload = "(unreadable bytes)" message_queue.put_nowait(utils.format_log_msg(message.topic, max_mqtt_topic_length, - payload)) #.replace("\n", " ~ ")) + payload)) # .replace("\n", " ~ ")) def publish_test_message(): + """ + Publish test message to see that app has started and mqtt client has connected (both successfully) + :return: None + """ time.sleep(2) mqttc.publish(topic="init", payload=datetime.datetime.now().isoformat()) diff --git a/requirements.txt b/requirements.txt index cb73df6..5cefdc0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ paho-mqtt~=1.5.1 dash~=1.20.0 +visdcc~=0.0.40 +protobuf~=3.15.8 -- GitLab