From c44c1575441dd99761c340421d4a575601813158 Mon Sep 17 00:00:00 2001 From: Bruno Ribeiro <59573315+brunus-reberes@users.noreply.github.com> Date: Thu, 20 Apr 2023 15:51:36 +0100 Subject: [PATCH] New version release v1.0.5 (#8) * chore: fix files names in Makefile * docs: change name of Glossary section to Main PEAK concepts * chore: change makefile argument * chore: formatted code with black and isort * fix: DF gourp hierarchy behavior when removing nodes * fix: cli not recognizing clones argument * chore: fix graphs in dashboard in data_extraction example * chore: bump version 1.0.2 -> 1.0.3 * chore: change random data to more realistic data in data_extration example * fix: 500 http error when requesting the group data through api * chore: add command to makefile * refact: change behaviour execution method name * chore: format with black and isort * fix: group tagging * chore: fix group tagging example * chore: change file paths format from windows to linux * chore: added env var to makefile * refact: changed logger level from error to exception in bootloader * fix: missing time atribute in message's metadata sent by Synchronizer * fix: typo in logger and wrong graph name sent to DF * fix: wait_for method in behaviors not allowing to add template * docs: clarified some text and added recent academic publications * chore: balck and isort * chore: bump version 1.0.3 -> 1.0.4 * refact: simplified graph creation * fix: drivers import * fix: modbusdriver update * refact: add CreateGraph behaviour to the agents * refact: remove unnucessary behaviours * docs: add and correct website pages * docs: correct docstrings * docs: correct peak module docstring * refact: change agents structure * refact: simplefied the PEAK's agent and behaviour structure * refact: deleted unecessary module * refect: remove properties model * refact: remove properties from the agent creation process * refact: correct agent structure from SyncAgent and Synchronizer Corrected docs as well. * fix: inexisting imports from init * docs: fix example import * docs: change 'single agent' example folder name * fix: agent print statements writing in terminal The stdout of each agent is redirected to the agent log file. The main process of PEAK logs usefull information in the terminal. * chores: isort and black * docs: corrected examples and added images * docs: corrected the code of the examples * docs: correct example 4 and 5 * refact: the core agents and behaviours, and the cli commands * refact: bootloader waits for agents asynchronously * chore: format code with isort, autoflake and black * chore: update pyproject.toml * chore: update makefile * chore: remove import headers from isort * chore: format code using isort, black autoflake * chore: bump version 1.0.4 -> 1.0.5 * chore: update makefile * chore: format code --- Makefile | 29 +- docs/diagrams.excalidraw | 4210 +++++++++++++++++ docs/foreword.md | 2 +- docs/how-to.md | 223 +- docs/installation.md | 11 +- docs/peak_communities.png | Bin 0 -> 241981 bytes docs/prerequistites.md | 127 +- docs/xmpp_config.md | 117 + .../agent.py | 8 +- examples/2_multiagent_system/mas.yaml | 7 + examples/2_multiagent_system/receiver.py | 12 + examples/2_multiagent_system/sender.py | 13 + examples/3_simple_community/agent.py | 29 + examples/4_community_tags/agent.py | 34 + examples/5_community_hierarchy/agent.py | 29 + .../agent_groups/group_hierarchy/agent.py | 38 - examples/agent_groups/group_tags/agents.yaml | 7 - .../agent_groups/group_tags/group_admin.py | 51 - .../agent_groups/group_tags/group_searcher.py | 25 - .../agent_groups/hello_world_group/agent.py | 25 - .../hello_world_group/agents.yaml | 13 - examples/chalenge_2/mas.yaml | 7 + examples/chalenge_2/searcher.py | 19 + examples/chalenge_2/tagger.py | 29 + examples/data_extraction/agent.py | 27 +- examples/data_extraction/graph_options.json | 19 +- examples/data_providers/dataset/agent.py | 3 +- pyproject.toml | 12 +- src/peak/__init__.py | 19 +- src/peak/__main__.py | 85 +- src/peak/agents.py | 270 +- src/peak/behaviours.py | 270 +- src/peak/bootloader.py | 130 +- src/peak/cli/df.py | 13 +- src/peak/cli/mas.py | 121 +- src/peak/core.py | 212 +- src/peak/drivers.py | 76 - src/peak/properties.py | 184 - 38 files changed, 5118 insertions(+), 1388 deletions(-) create mode 100644 docs/diagrams.excalidraw create mode 100644 docs/peak_communities.png create mode 100644 docs/xmpp_config.md rename examples/{hello_world_agent => 1_simple_agent}/agent.py (51%) create mode 100644 examples/2_multiagent_system/mas.yaml create mode 100644 examples/2_multiagent_system/receiver.py create mode 100644 examples/2_multiagent_system/sender.py create mode 100644 examples/3_simple_community/agent.py create mode 100644 examples/4_community_tags/agent.py create mode 100644 examples/5_community_hierarchy/agent.py delete mode 100644 examples/agent_groups/group_hierarchy/agent.py delete mode 100644 examples/agent_groups/group_tags/agents.yaml delete mode 100644 examples/agent_groups/group_tags/group_admin.py delete mode 100644 examples/agent_groups/group_tags/group_searcher.py delete mode 100644 examples/agent_groups/hello_world_group/agent.py delete mode 100644 examples/agent_groups/hello_world_group/agents.yaml create mode 100644 examples/chalenge_2/mas.yaml create mode 100644 examples/chalenge_2/searcher.py create mode 100644 examples/chalenge_2/tagger.py delete mode 100644 src/peak/drivers.py delete mode 100644 src/peak/properties.py diff --git a/Makefile b/Makefile index 8691d74..59ddbed 100644 --- a/Makefile +++ b/Makefile @@ -1,31 +1,12 @@ DOMAIN?=localhost -all: single_agent single_group group_hierarchy group_tags data_extraction dataset simulation - -single_agent: - peak run examples/hello_world_agent/agent.py -j agent@$(DOMAIN) - -single_group: - peak start examples/agent_groups/hello_world_group/agents.yaml - -group_hierarchy: - peak run examples/agent_groups/group_hierarchy/agent.py -j agent@$(DOMAIN) - -group_tags: - peak start examples/agent_groups/group_tags/agents.yaml - -data_extraction: - peak run examples/data_extraction/agent.py -j agent@$(DOMAIN) -c 4 - -dataset: - peak run examples/data_providers/dataset/agent.py -j agent@$(DOMAIN) -p examples/data_providers/dataset/dataset.py - -simulation: - peak start examples/simulation/start.yaml +lint: + pylint src/peak/ format: + autoflake --remove-all-unused-imports --remove-unused-variables --expand-star-imports --ignore-init-module-imports -ri . + isort --profile black . black . - isort . patch: format python -m bumpver update --patch @@ -39,4 +20,4 @@ major: format publish: format python -m build twine check dist/* - twine upload dist/* \ No newline at end of file + twine upload -u gecad-group dist/* \ No newline at end of file diff --git a/docs/diagrams.excalidraw b/docs/diagrams.excalidraw new file mode 100644 index 0000000..43654c8 --- /dev/null +++ b/docs/diagrams.excalidraw @@ -0,0 +1,4210 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 289, + "versionNonce": 331610693, + "isDeleted": false, + "id": "Ammo_wb2P_GvjN8XU8BMa", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 29.99096613956476, + "y": 100.6868359708892, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 148.800048828125, + "height": 400.8000183105468, + "seed": 1487668477, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 231, + "versionNonce": 1984133259, + "isDeleted": false, + "id": "LatY1eoHDyFdg8Lfw6Isf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 33.19091731143976, + "y": 100.28682681561577, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 74.703125, + "height": 32.199999999999996, + "seed": 224423133, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 2, + "text": "PEAK", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "PEAK", + "lineHeight": 1.15, + "baseline": 25 + }, + { + "type": "rectangle", + "version": 280, + "versionNonce": 473328037, + "isDeleted": false, + "id": "CCt1j8atSkN_WZzkaoYzl", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 191.59094172550226, + "y": -13.113151822079658, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 518.4000244140625, + "height": 108, + "seed": 942021939, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 215, + "versionNonce": 939142955, + "isDeleted": false, + "id": "51v6I2e4vB4giWr9yvuCv", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 194.79089289737726, + "y": -10.113151822079658, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 219.4609375, + "height": 32.199999999999996, + "seed": 323792851, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 2, + "text": "PEAK Dashboard", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "PEAK Dashboard", + "lineHeight": 1.15, + "baseline": 25 + }, + { + "type": "diamond", + "version": 11421, + "versionNonce": 2137255173, + "isDeleted": false, + "id": "o3nn6FeuvSCbbfYVY-7UN", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 236.590880690346, + "y": 269.236744418155, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 718611357, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 296, + "versionNonce": 182522315, + "isDeleted": false, + "id": "zf0ONpsKtvD9Ji_qYaHZ_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 337.99096613956476, + "y": 383.2869031095611, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 55.20001220703125, + "height": 56.79998779296875, + "seed": 585061523, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [ + { + "id": "Ko81qNB0tJ4Na1qN4ZYYV", + "type": "arrow" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 325, + "versionNonce": 1556743269, + "isDeleted": false, + "id": "sMelmyft_ih6d4CRUeY-4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 417.19088679386164, + "y": 318.6868359708892, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 55.20001220703125, + "height": 56.79998779296875, + "seed": 1365603507, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [ + { + "id": "sTgaBfpx97zT_OBRFPWBQ", + "type": "arrow" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 311, + "versionNonce": 806527083, + "isDeleted": false, + "id": "kWPfUWcROtPRNaYUBxznB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 437.79098445011164, + "y": 426.08679934979546, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 55.20001220703125, + "height": 56.79998779296875, + "seed": 276503091, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [ + { + "id": "LJ1TgF9Zk5BGZFOtam4u-", + "type": "arrow" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11460, + "versionNonce": 137768901, + "isDeleted": false, + "id": "LrrbAm6m4rZvY0wfFe138", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 263.190978346596, + "y": 203.08676883221733, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 2103582355, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11459, + "versionNonce": 1633245963, + "isDeleted": false, + "id": "7BfY4k5zUDgfqLfxgQC8k", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 308.19091731143976, + "y": 267.08689090252983, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 407046173, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11444, + "versionNonce": 2143678245, + "isDeleted": false, + "id": "SzIaMpKcJPD-4LVT2JPVf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 525.7909539325335, + "y": 214.0868603849517, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 1197921331, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11445, + "versionNonce": 992985515, + "isDeleted": false, + "id": "sAZefb_TH9e5eqOaainNS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 529.590880690346, + "y": 291.2869336271392, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 1715862653, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11445, + "versionNonce": 568771205, + "isDeleted": false, + "id": "qIpwBfiA46-UUfqIIovi7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 602.9909051044084, + "y": 263.0868603849517, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 109303251, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11454, + "versionNonce": 1723880523, + "isDeleted": false, + "id": "VZLEnLryx_kGBCQCfWwxS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 640.3909295184709, + "y": 457.28684207440483, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 1957389533, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11448, + "versionNonce": 592849381, + "isDeleted": false, + "id": "UKbgSUxsbMP0g_geHmQN7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 582.3909295184709, + "y": 397.88681766034233, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 164602739, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11446, + "versionNonce": 65377003, + "isDeleted": false, + "id": "hMq1J80VocLVwrodluQ8U", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 564.7909539325334, + "y": 474.28684207440483, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 1996800317, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11479, + "versionNonce": 1159832901, + "isDeleted": false, + "id": "3pjDF8gGB4RzOrto5Xm9y", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 374.59094172550226, + "y": 555.4869153165923, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 2039528723, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11459, + "versionNonce": 1395260811, + "isDeleted": false, + "id": "eMeFSLRTMcXGja0p8zGAV", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 311.9909051044085, + "y": 509.2869031095611, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 850629021, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11475, + "versionNonce": 1252439205, + "isDeleted": false, + "id": "UYD9rJya4ha2wHKMLSuTJ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 303.39086848331476, + "y": 575.4868542814361, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 826122931, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11473, + "versionNonce": 533206059, + "isDeleted": false, + "id": "SsbHIasWe5M6bQh9_pd_G", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 247.19091731143976, + "y": 522.4869153165923, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 1156142589, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 262, + "versionNonce": 798152709, + "isDeleted": false, + "id": "ww6QNn8dQTl_wdVFzbZ4R", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 505.19103938175226, + "y": 198.88687869549858, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 162.4000244140625, + "height": 154.39999389648438, + "seed": 26436509, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [ + { + "id": "rwod_ZdARKfhCUpuIyZrw", + "type": "arrow" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 234, + "versionNonce": 1772771019, + "isDeleted": false, + "id": "eYHPYiV8__T-9pLWa5719", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 211.5910027606585, + "y": 197.28684207440483, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 160, + "height": 140.80001831054688, + "seed": 1216982323, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [ + { + "id": "e1sbThKwyru3I_XIPHc-4", + "type": "arrow" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 236, + "versionNonce": 501452645, + "isDeleted": false, + "id": "6wPfWpCKnRelleV7Efis0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 546.7908928973773, + "y": 384.4868542814361, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 160.800048828125, + "height": 161.5999755859375, + "seed": 1686200051, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [ + { + "id": "LJ1TgF9Zk5BGZFOtam4u-", + "type": "arrow" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 272, + "versionNonce": 1332430187, + "isDeleted": false, + "id": "qUq0I4CaC33VEIUEb9U2-", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0.2373035763980429, + "x": 224.390929518471, + "y": 493.28684207440483, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 219.20001220703125, + "height": 148, + "seed": 1957272125, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [ + { + "id": "Ko81qNB0tJ4Na1qN4ZYYV", + "type": "arrow" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 298, + "versionNonce": 602299077, + "isDeleted": false, + "id": "e1sbThKwyru3I_XIPHc-4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 325.190978346596, + "y": 334.0868603849517, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 28.79998779296875, + "height": 51.999969482421875, + "seed": 341011581, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "startBinding": { + "elementId": "eYHPYiV8__T-9pLWa5719", + "focus": 0.03568049694841785, + "gap": 2.329978891243414 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": "triangle", + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 28.79998779296875, + 51.999969482421875 + ] + ] + }, + { + "type": "arrow", + "version": 372, + "versionNonce": 945119243, + "isDeleted": false, + "id": "Ko81qNB0tJ4Na1qN4ZYYV", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 341.99096613956476, + "y": 491.68686648846733, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 15.20001220703125, + "height": 46.4000244140625, + "seed": 2021565843, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "startBinding": { + "elementId": "qUq0I4CaC33VEIUEb9U2-", + "focus": -0.1456171397377326, + "gap": 1.6672744468060472 + }, + "endBinding": { + "elementId": "zf0ONpsKtvD9Ji_qYaHZ_", + "focus": -0.08950429577282927, + "gap": 6.282550970303841 + }, + "lastCommittedPoint": null, + "startArrowhead": "triangle", + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 15.20001220703125, + -46.4000244140625 + ] + ] + }, + { + "type": "arrow", + "version": 383, + "versionNonce": 708360741, + "isDeleted": false, + "id": "LJ1TgF9Zk5BGZFOtam4u-", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 546.7910149676898, + "y": 458.0868298673736, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 51.2000732421875, + "height": 1.5999755859375, + "seed": 2053131677, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "startBinding": { + "elementId": "6wPfWpCKnRelleV7Efis0", + "focus": 0.05798630412692896, + "gap": 1 + }, + "endBinding": { + "elementId": "kWPfUWcROtPRNaYUBxznB", + "focus": 0.03717752657396646, + "gap": 2.662737692794572 + }, + "lastCommittedPoint": null, + "startArrowhead": "triangle", + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + -51.2000732421875, + -1.5999755859375 + ] + ] + }, + { + "type": "arrow", + "version": 279, + "versionNonce": 1046685355, + "isDeleted": false, + "id": "rwod_ZdARKfhCUpuIyZrw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 512.3909905536273, + "y": 308.4868542814361, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 44.800048828125, + "height": 24, + "seed": 851865693, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "startBinding": { + "elementId": "ww6QNn8dQTl_wdVFzbZ4R", + "focus": 0.08173687828573166, + "gap": 1 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": "triangle", + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + -44.800048828125, + 24 + ] + ] + }, + { + "type": "line", + "version": 187, + "versionNonce": 700984709, + "isDeleted": false, + "id": "M4hTRM6y8jYofYh6y1XSI", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 385.190978346596, + "y": 391.68686648846733, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 37.5999755859375, + "height": 25.60003662109375, + "seed": 410803229, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 37.5999755859375, + -25.60003662109375 + ] + ] + }, + { + "type": "line", + "version": 199, + "versionNonce": 1480530251, + "isDeleted": false, + "id": "qIoB4-szeF5RW84V01lax", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 451.59094172550226, + "y": 374.0868298673736, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 9.60003662109375, + "height": 52.800048828125, + "seed": 942929245, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 9.60003662109375, + 52.800048828125 + ] + ] + }, + { + "type": "line", + "version": 188, + "versionNonce": 567424229, + "isDeleted": false, + "id": "710YRQp2FzuRWve90XTDf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 388.390929518471, + "y": 426.8868786954986, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 52, + "height": 20, + "seed": 1553728733, + "groupIds": [ + "62N_C1x1PyTQeHKBki_uS" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 52, + 20 + ] + ] + }, + { + "type": "diamond", + "version": 178, + "versionNonce": 1128511467, + "isDeleted": false, + "id": "bqzR_Lczq7OepV6gf1WhA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 412.390929518471, + "y": 155.28684207440483, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 62.4000244140625, + "height": 65.59994506835938, + "seed": 475940435, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "sTgaBfpx97zT_OBRFPWBQ", + "type": "arrow" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 164, + "versionNonce": 1306808389, + "isDeleted": false, + "id": "sj31VqM4n0VOTEse4Im3D", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 409.99096613956476, + "y": 172.88684817792046, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 213.60003662109375, + "height": 76, + "seed": 847030547, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -213.60003662109375, + -76 + ] + ] + }, + { + "type": "line", + "version": 184, + "versionNonce": 763069067, + "isDeleted": false, + "id": "Pfm4kEmruqOggLSXrMAHm", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 475.59094172550226, + "y": 177.6868359708892, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 232.00006103515625, + "height": 76, + "seed": 1502053235, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 232.00006103515625, + -76 + ] + ] + }, + { + "type": "arrow", + "version": 226, + "versionNonce": 467402661, + "isDeleted": false, + "id": "sTgaBfpx97zT_OBRFPWBQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 443.59094172550226, + "y": 225.6868359708892, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 0.79998779296875, + "height": 87.20004272460938, + "seed": 702551059, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "startBinding": { + "elementId": "bqzR_Lczq7OepV6gf1WhA", + "focus": 0.011056049899565712, + "gap": 3.308261863658519 + }, + "endBinding": { + "elementId": "sMelmyft_ih6d4CRUeY-4", + "focus": -0.0031233130553721864, + "gap": 5.8024090244794415 + }, + "lastCommittedPoint": null, + "startArrowhead": "triangle", + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 0.79998779296875, + 87.20004272460938 + ] + ] + }, + { + "type": "rectangle", + "version": 603, + "versionNonce": 530643243, + "isDeleted": false, + "id": "aVyqVkriR5nGOGeaBLEBp", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 42.79098445011164, + "y": 260.88697024823296, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 124, + "height": 56, + "seed": 1199398899, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Dvss2sGeXX5T09A4dqIrG" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 514, + "versionNonce": 1642513157, + "isDeleted": false, + "id": "Dvss2sGeXX5T09A4dqIrG", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 60.87496882511164, + "y": 265.88697024823296, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 87.83203125, + "height": 46, + "seed": 493317213, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Agent \nExecution", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "aVyqVkriR5nGOGeaBLEBp", + "originalText": "Agent Execution", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "rectangle", + "version": 643, + "versionNonce": 656317387, + "isDeleted": false, + "id": "RQT7iJzJ0DnXZZmrsgfmJ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 44.19091731143976, + "y": 320.0867383146392, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 124, + "height": 56, + "seed": 1167473331, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Ynga9IFPw0UFNYPEb1HF1" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 567, + "versionNonce": 1417812581, + "isDeleted": false, + "id": "Ynga9IFPw0UFNYPEb1HF1", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 72.85107356143976, + "y": 325.0867383146392, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 66.6796875, + "height": 46, + "seed": 839269885, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Debug \nSystem", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "RQT7iJzJ0DnXZZmrsgfmJ", + "originalText": "Debug System", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "rectangle", + "version": 698, + "versionNonce": 1074719339, + "isDeleted": false, + "id": "EhxodaY-q9VWgtjafqfny", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 43.39087134433771, + "y": 379.88677188397514, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 124, + "height": 56, + "seed": 1832120317, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "CelQGqO--xU0MPy4ZumMT" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 633, + "versionNonce": 1014728133, + "isDeleted": false, + "id": "CelQGqO--xU0MPy4ZumMT", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 67.02173071933771, + "y": 384.88677188397514, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 76.73828125, + "height": 46, + "seed": 608229971, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Logging \nSystem", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "EhxodaY-q9VWgtjafqfny", + "originalText": "Logging System", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "rectangle", + "version": 687, + "versionNonce": 2134664459, + "isDeleted": false, + "id": "yUbhTTI1Zb_kaHTvUWQ7P", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 42.59089881015802, + "y": 137.28679629803764, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 124, + "height": 56, + "seed": 710198227, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "cFKXHv5u-5tHTudFs2Umw" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 640, + "versionNonce": 1827702053, + "isDeleted": false, + "id": "cFKXHv5u-5tHTudFs2Umw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 54.58601599765802, + "y": 142.28679629803764, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 100.009765625, + "height": 46, + "seed": 1697142493, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Agent \nFramework", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "yUbhTTI1Zb_kaHTvUWQ7P", + "originalText": "Agent Framework", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "rectangle", + "version": 285, + "versionNonce": 2082573227, + "isDeleted": false, + "id": "FGOfc4M-XTJhGKhFf4wX0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 197.19099646640802, + "y": 30.286811556826592, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 189, + "height": 56, + "seed": 27745085, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "HGTbDlzVg5tcynND4uZma" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 202, + "versionNonce": 2035193989, + "isDeleted": false, + "id": "HGTbDlzVg5tcynND4uZma", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 202.52107459140802, + "y": 35.28681155682659, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 178.33984375, + "height": 46, + "seed": 1009437139, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Agent Communities'\nVisualization", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "FGOfc4M-XTJhGKhFf4wX0", + "originalText": "Agent Communities' Visualization", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "rectangle", + "version": 343, + "versionNonce": 2062081611, + "isDeleted": false, + "id": "beLRE0GezhIMh4-dBoBH8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 389.69090491367365, + "y": 31.086814608584405, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 176, + "height": 56, + "seed": 1044157203, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "9czlNXTkWpD2XOZHIS_N8" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 327, + "versionNonce": 726876133, + "isDeleted": false, + "id": "9czlNXTkWpD2XOZHIS_N8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 397.09031897617365, + "y": 36.086814608584405, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 161.201171875, + "height": 46, + "seed": 1071127453, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Data Analysis and\nVisualization", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "beLRE0GezhIMh4-dBoBH8", + "originalText": "Data Analysis and Visualization", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "rectangle", + "version": 488, + "versionNonce": 1449975019, + "isDeleted": false, + "id": "Fz95OvS0BE5QHOq_MOSXD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 570.790880499611, + "y": 30.886817660342217, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 133, + "height": 56, + "seed": 1165761683, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Y0G_CJtIrErI8FVxtpLmp" + } + ], + "updated": 1681897721714, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 460, + "versionNonce": 957086533, + "isDeleted": false, + "id": "Y0G_CJtIrErI8FVxtpLmp", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 581.328966437111, + "y": 35.88681766034222, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 111.923828125, + "height": 46, + "seed": 1968171549, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721714, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Services \nVisualization", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Fz95OvS0BE5QHOq_MOSXD", + "originalText": "Services Visualization", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "rectangle", + "version": 675, + "versionNonce": 708315019, + "isDeleted": false, + "id": "wt_qI-3H7iR0vE_icu30_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 43.19090491367365, + "y": 439.28682681561577, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 124, + "height": 56, + "seed": 289190931, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "NCMHhXJ7w_vCEuHUKbLXm" + } + ], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 615, + "versionNonce": 987122341, + "isDeleted": false, + "id": "NCMHhXJ7w_vCEuHUKbLXm", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 72.03172522617365, + "y": 455.78682681561577, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 66.318359375, + "height": 23, + "seed": 891718301, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "SPADE", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "wt_qI-3H7iR0vE_icu30_", + "originalText": "SPADE", + "lineHeight": 1.15, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 717, + "versionNonce": 1015340587, + "isDeleted": false, + "id": "v1rXLFEfgiQR61l3vgDsd", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 43.99095374179865, + "y": 200.08681460858452, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 124, + "height": 56, + "seed": 1432569853, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "o9gfWuAbibKHXGQrKSFrk" + } + ], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 688, + "versionNonce": 1633660421, + "isDeleted": false, + "id": "o9gfWuAbibKHXGQrKSFrk", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 54.84349280429865, + "y": 205.08681460858452, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 102.294921875, + "height": 46, + "seed": 458424915, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Predefined \nAgents", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "v1rXLFEfgiQR61l3vgDsd", + "originalText": "Predefined Agents", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "line", + "version": 740, + "versionNonce": 675143883, + "isDeleted": false, + "id": "MrOz5ZqLCixd2PtvDW9wQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 23.448448054741107, + "y": 431.40708773230654, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 203.71506253627592, + "height": 230.62628228185463, + "seed": 851072316, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0.14248127299504176, + 75.87976960088713 + ], + [ + 160.94253010112004, + 76.67975739385588 + ], + [ + 161.7425178940888, + -0.9202181920816201 + ], + [ + 181.4875610106443, + -0.9202181920816201 + ], + [ + 181.7425178940888, + 229.47974518682463 + ], + [ + -21.780580048079173, + 229.706064089773 + ], + [ + -21.97254464218713, + 0.14248127299515545 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "text", + "version": 196, + "versionNonce": 1806017893, + "isDeleted": false, + "id": "O12vhcISQhaOKB3k142JA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 25.45001605175446, + "y": 510.2164971913294, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 82.255859375, + "height": 23, + "seed": 475056900, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "PEAK FL", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "PEAK FL", + "lineHeight": 1.15, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 772, + "versionNonce": 1191022443, + "isDeleted": false, + "id": "SqycWKlvF9NmhFapa-Lk5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 26.79088049961115, + "y": 544.0868451261626, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 156, + "height": 42, + "seed": 696131588, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "mFZqKxGnf_rpzOT6WTy7d" + } + ], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 723, + "versionNonce": 1559605445, + "isDeleted": false, + "id": "mFZqKxGnf_rpzOT6WTy7d", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 50.31334143711115, + "y": 553.5868451261626, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 108.955078125, + "height": 23, + "seed": 1291244092, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Client Agent", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "SqycWKlvF9NmhFapa-Lk5", + "originalText": "Client Agent", + "lineHeight": 1.15, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 782, + "versionNonce": 1527671307, + "isDeleted": false, + "id": "zxR0nV6AUsSG_1n57sIiZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 28.7909415347674, + "y": 603.8868329191314, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 156, + "height": 42, + "seed": 2036947716, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "NGwutN0TlNM_qHxDZAQhO" + } + ], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 738, + "versionNonce": 1928155173, + "isDeleted": false, + "id": "NGwutN0TlNM_qHxDZAQhO", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 48.4266837222674, + "y": 613.3868329191314, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 116.728515625, + "height": 23, + "seed": 816891708, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Server Agent", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "zxR0nV6AUsSG_1n57sIiZ", + "originalText": "Server Agent", + "lineHeight": 1.15, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 417, + "versionNonce": 2037051563, + "isDeleted": false, + "id": "Gfl43cxYA06_lNlxVFSEI", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 869.9240904839305, + "y": 6.800067048906271, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 168.60000610351562, + "height": 627.800079345703, + "seed": 1355736511, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 281, + "versionNonce": 1534722949, + "isDeleted": false, + "id": "MBfwAsDUgQ30t3d4zBFmI", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 874.7168499705379, + "y": 11.164455760018313, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 93.43359375, + "height": 32.199999999999996, + "seed": 1343271985, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 2, + "text": "Legend", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Legend", + "lineHeight": 1.15, + "baseline": 25 + }, + { + "type": "diamond", + "version": 647, + "versionNonce": 868066123, + "isDeleted": false, + "id": "qu3Nb750Xx_4VxKBQWdiD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 884.5168225047171, + "y": 415.21444889356326, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 35.400054931640625, + "height": 33.59989929199219, + "seed": 1460769489, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11733, + "versionNonce": 718883557, + "isDeleted": false, + "id": "3EZpskyAmYhLNaalI9sF8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 889.2668987986624, + "y": 68.21449466993033, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 29.39996337890625, + "height": 31.5999755859375, + "seed": 1961267569, + "groupIds": [ + "WXznj9p8u9MJ2SNoWZ7GA", + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 331, + "versionNonce": 39911915, + "isDeleted": false, + "id": "2gqDxvqpcugkKiO0eso1D", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 950.9474713084285, + "y": 62.86439853955915, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 53.359375, + "height": 46, + "seed": 1713656127, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "PEAK\nagent", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "PEAK\nagent", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "ellipse", + "version": 695, + "versionNonce": 295216709, + "isDeleted": false, + "id": "jANNEfS926oEqs5ZLTKes", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 891.6168133494436, + "y": 209.1144351606531, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 29.200027465820312, + "height": 29.80001831054688, + "seed": 676262225, + "groupIds": [ + "UpUVK241C1RSIpwIxelxg", + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 419, + "versionNonce": 1851755659, + "isDeleted": false, + "id": "Z88BTw2aqCNGXtoSTFXcX", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 948.1740338084285, + "y": 202.51442905713748, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 58.90625, + "height": 46, + "seed": 686513, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "XMPP\nServer", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "XMPP\nServer", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "ellipse", + "version": 686, + "versionNonce": 1385534885, + "isDeleted": false, + "id": "ovSf_kOsFZUIR7i_7eudD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 876.2169262644827, + "y": 286.270885041542, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 16.539834461603547, + "height": 17.019242531927173, + "seed": 1182790559, + "groupIds": [ + "6Y2j3Uriak45prL-a0x-4", + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 715, + "versionNonce": 2071184171, + "isDeleted": false, + "id": "hu51coXH-AgzEtCqZUB_n", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 899.9479640780462, + "y": 266.91446872998904, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 16.539834461603547, + "height": 17.019242531927173, + "seed": 1482807761, + "groupIds": [ + "6Y2j3Uriak45prL-a0x-4", + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 701, + "versionNonce": 2029885701, + "isDeleted": false, + "id": "G1Tmm7gCIqqFdqkDdHSse", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 906.1204664291206, + "y": 299.0952155169093, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 16.539834461603547, + "height": 17.019242531927173, + "seed": 983443391, + "groupIds": [ + "6Y2j3Uriak45prL-a0x-4", + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 577, + "versionNonce": 1376265675, + "isDeleted": false, + "id": "d3G_KtNeXuzQGqr-DZkgU", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 890.3596837979769, + "y": 288.78780484310937, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 11.266254246815642, + "height": 7.670657142897243, + "seed": 1669887921, + "groupIds": [ + "6Y2j3Uriak45prL-a0x-4", + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 11.266254246815642, + -7.670657142897243 + ] + ] + }, + { + "type": "line", + "version": 589, + "versionNonce": 1313747045, + "isDeleted": false, + "id": "NgaaMVKt2HU1RhIGUQPvw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 910.255411328337, + "y": 283.5142246283217, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 2.8765032866785685, + "height": 15.820722356118107, + "seed": 1361106911, + "groupIds": [ + "6Y2j3Uriak45prL-a0x-4", + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.8765032866785685, + 15.820722356118107 + ] + ] + }, + { + "type": "line", + "version": 578, + "versionNonce": 691483755, + "isDeleted": false, + "id": "KhcoI2boZSxgyyNP8N20F", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 891.3184999386242, + "y": 299.33494698443974, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 15.58100003271069, + "height": 5.992692320273343, + "seed": 1876973969, + "groupIds": [ + "6Y2j3Uriak45prL-a0x-4", + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 15.58100003271069, + 5.992692320273343 + ] + ] + }, + { + "type": "text", + "version": 481, + "versionNonce": 273996741, + "isDeleted": false, + "id": "pOybP9lSc_h7AsVet66N_", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 948.1740338084285, + "y": 271.5145053510828, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 58.90625, + "height": 46, + "seed": 2018731569, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Server\ncluster", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Server\ncluster", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "diamond", + "version": 11721, + "versionNonce": 1892380427, + "isDeleted": false, + "id": "45sMILWGgv_2JD-NZSAxm", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 889.0168835398733, + "y": 141.46446415235232, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 28.399978637695312, + "height": 31.5999755859375, + "seed": 55119487, + "groupIds": [ + "HXWCUgHBjBWw4x5ryk10j", + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 380, + "versionNonce": 1731140389, + "isDeleted": false, + "id": "sWKv7qqjeKT42G1_xbab_", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 936.4992291209285, + "y": 136.0144595747156, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 82.255859375, + "height": 46, + "seed": 345740863, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "PEAK FL\nagent", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "PEAK FL\nagent", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "ellipse", + "version": 643, + "versionNonce": 994006443, + "isDeleted": false, + "id": "eHtNKXw6-u3khuDDrePnp", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 885.5169598338186, + "y": 352.3144397382898, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 30.400054931640625, + "height": 32.39990997314453, + "seed": 2120299167, + "groupIds": [ + "_F-fTw7KxAv3BsOyvwaOr", + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 532, + "versionNonce": 457862789, + "isDeleted": false, + "id": "hLTBsccaMAGJqdmqxNgb-", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 929.2824322459285, + "y": 346.26449772168826, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 96.689453125, + "height": 46, + "seed": 2140671569, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Agent\ncommunity", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Agent\ncommunity", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "text", + "version": 601, + "versionNonce": 1740466251, + "isDeleted": false, + "id": "2VBo66gWGNVKm1qWCBf_O", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 934.2824322459285, + "y": 402.0145053510828, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 86.689453125, + "height": 69, + "seed": 1754213087, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Directory\nFacilitator\nagent", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Directory\nFacilitator\nagent", + "lineHeight": 1.15, + "baseline": 64 + }, + { + "type": "line", + "version": 1091, + "versionNonce": 191124965, + "isDeleted": false, + "id": "5_Vuu_ND0NDk-ZeGaxcTH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 885.2115746889638, + "y": 478.60540983662713, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 35.71503201869784, + "height": 38.62623650548744, + "seed": 375552095, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0.02497961203127208, + 12.708655308434315 + ], + [ + 28.216213097684786, + 12.842640548047854 + ], + [ + 28.356465807939625, + -0.15412192041736483 + ], + [ + 31.818138392865805, + -0.15412192041736483 + ], + [ + 31.86283706728997, + 38.43420976613768 + ], + [ + -3.8185399946282232, + 38.472114585070074 + ], + [ + -3.852194951407873, + 0.02386334850417352 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "text", + "version": 649, + "versionNonce": 203433707, + "isDeleted": false, + "id": "-MNqKh1zm_QEX2YkEbaIO", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 949.8293072459285, + "y": 490.01447483350466, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 55.595703125, + "height": 23, + "seed": 1930800241, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Plugin", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Plugin", + "lineHeight": 1.15, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 639, + "versionNonce": 1653231941, + "isDeleted": false, + "id": "cY3zpPDG-R36el1AfEmHD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 883.8169628855769, + "y": 538.3644580488367, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 29.80003356933595, + "height": 28.800010681152337, + "seed": 202359071, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 589, + "versionNonce": 519180683, + "isDeleted": false, + "id": "vd5mmIOac4tW334tn90ao", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 928.1496197459285, + "y": 533.0144900922937, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 98.955078125, + "height": 46, + "seed": 1703768735, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Software\ncomponent", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Software\ncomponent", + "lineHeight": 1.15, + "baseline": 41 + }, + { + "type": "rectangle", + "version": 1055, + "versionNonce": 236576933, + "isDeleted": false, + "id": "0DXlu4et9eOfeNsDI3KlA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 880.2169110056941, + "y": 589.2644824628992, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 33.999954223632805, + "height": 34.99995422363281, + "seed": 1337600113, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 596, + "versionNonce": 1634576427, + "isDeleted": false, + "id": "vc1C17zZK45iS6ERxrSnX", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 921.4894634959285, + "y": 595.014421427743, + "strokeColor": "#000000", + "backgroundColor": "#000000", + "width": 112.275390625, + "height": 23, + "seed": 1389319089, + "groupIds": [ + "EcLJKWMh__-oDvY2eh0qb" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897721715, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Functionality", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Functionality", + "lineHeight": 1.15, + "baseline": 18 + }, + { + "type": "diamond", + "version": 11435, + "versionNonce": 1314759947, + "isDeleted": false, + "id": "t0tCHzUN6hcB8CVZdzWp3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 348.76405777367904, + "y": 1220.506275668155, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 718611357, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897849484, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 298, + "versionNonce": 1831115755, + "isDeleted": false, + "id": "nQ-rdtFel6u5plaJGUHOQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 464.39591405623116, + "y": 1315.904090609561, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 55.20001220703125, + "height": 56.79998779296875, + "seed": 585061523, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "hD96cqfCMh9_DbP9i7uY8", + "type": "arrow" + } + ], + "updated": 1681897743207, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 331, + "versionNonce": 738537093, + "isDeleted": false, + "id": "EIPWGNjkX6Qko90HqiU6x", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 543.595834710528, + "y": 1251.3040234708892, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 55.20001220703125, + "height": 56.79998779296875, + "seed": 1365603507, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "7wD72qfU8x02rhzOXHlKd", + "type": "arrow" + } + ], + "updated": 1681897853459, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 315, + "versionNonce": 107718475, + "isDeleted": false, + "id": "4eSku6RRpRkssxtPyVQ8y", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 564.195932366778, + "y": 1358.7039868497955, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 55.20001220703125, + "height": 56.79998779296875, + "seed": 276503091, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "9Qj79ByzVAy4UK13zWQzA", + "type": "arrow" + } + ], + "updated": 1681897828268, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11474, + "versionNonce": 193152293, + "isDeleted": false, + "id": "EaBKAQS2pJ3z9mKErfV4x", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 375.36415542992904, + "y": 1154.3563000822173, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 2103582355, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897849484, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11473, + "versionNonce": 1427819435, + "isDeleted": false, + "id": "a9xeomdXhA5yj8uafV-CZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 420.3640943947728, + "y": 1218.3564221525298, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 407046173, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897849484, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11467, + "versionNonce": 1674774597, + "isDeleted": false, + "id": "BOIbHYRHrBYCiK_xupYyh", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 729.3443393491999, + "y": 1115.4735791349517, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 1197921331, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897768861, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11468, + "versionNonce": 1022642827, + "isDeleted": false, + "id": "FFol3MvY5IWKvFKDmOdy1", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 733.1442661070124, + "y": 1192.6736523771392, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 1715862653, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897768861, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11468, + "versionNonce": 1932769189, + "isDeleted": false, + "id": "H8NI54QgtTNljOW9jzU4m", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 806.5442905210748, + "y": 1164.4735791349517, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 109303251, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897768861, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11478, + "versionNonce": 1421918507, + "isDeleted": false, + "id": "WAUa_y6EcJGN0XzB4bk3H", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 843.9443149351373, + "y": 1358.6735608244048, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 1957389533, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897768861, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11472, + "versionNonce": 1703377669, + "isDeleted": false, + "id": "8KZVLAEoktKG8nqWpGqSH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 785.9443149351373, + "y": 1299.2735364103423, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 164602739, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897768861, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11470, + "versionNonce": 889545675, + "isDeleted": false, + "id": "KxUz_f-mdDQBg9ym2Y0TC", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 768.3443393491998, + "y": 1375.6735608244048, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 1996800317, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897768861, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11482, + "versionNonce": 102468043, + "isDeleted": false, + "id": "cQ9YS5X4tvIoO0k-Z8cSr", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 500.99588964216866, + "y": 1488.1041028165923, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 2039528723, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897750729, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11462, + "versionNonce": 580062309, + "isDeleted": false, + "id": "Y17mQS3II2b_EK49Dir7P", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 438.3958530210749, + "y": 1441.904090609561, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 850629021, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897750729, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11478, + "versionNonce": 1295915115, + "isDeleted": false, + "id": "jCR8T84dPj8wNO-OKGWRD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 429.79581639998116, + "y": 1508.104041781436, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 826122931, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897750729, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 11476, + "versionNonce": 1067333573, + "isDeleted": false, + "id": "wA7SzXXsGe9cGoNTL999Y", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 373.59586522810616, + "y": 1455.1041028165923, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 50.4000244140625, + "height": 51.5999755859375, + "seed": 1156142589, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897750729, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 287, + "versionNonce": 786308651, + "isDeleted": false, + "id": "9DdxQ0d8TYxT8Fy5figNM", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 707.2600497984187, + "y": 1100.2735974454986, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 162.4000244140625, + "height": 154.39999389648438, + "seed": 26436509, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897850180, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 248, + "versionNonce": 931747973, + "isDeleted": false, + "id": "buPqqyTR-9srTE1oG1u-0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 323.76417984399154, + "y": 1148.5563733244048, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 160, + "height": 140.80001831054688, + "seed": 1216982323, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "7wD72qfU8x02rhzOXHlKd", + "type": "arrow" + } + ], + "updated": 1681897849484, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 260, + "versionNonce": 784885253, + "isDeleted": false, + "id": "908_Q71Hh654jdqqACBUw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 750.3442783140437, + "y": 1285.873573031436, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 160.800048828125, + "height": 161.5999755859375, + "seed": 1686200051, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897772917, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 274, + "versionNonce": 951610251, + "isDeleted": false, + "id": "MvzrG9xDr5qX2f2T1mybp", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "angle": 0.2373035763980429, + "x": 350.7958774351374, + "y": 1425.9040295744048, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 219.20001220703125, + "height": 148, + "seed": 1957272125, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "hD96cqfCMh9_DbP9i7uY8", + "type": "arrow" + } + ], + "updated": 1681897743207, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 400, + "versionNonce": 1296656107, + "isDeleted": false, + "id": "7wD72qfU8x02rhzOXHlKd", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 483.54254084659567, + "y": 1259.3863395516184, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 55.635925292968864, + "height": 10.40491739908839, + "seed": 341011581, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897853660, + "link": null, + "locked": false, + "startBinding": { + "elementId": "buPqqyTR-9srTE1oG1u-0", + "focus": 0.3544417158495295, + "gap": 11.656980655058717 + }, + "endBinding": { + "elementId": "EIPWGNjkX6Qko90HqiU6x", + "focus": 0.1359744320384528, + "gap": 5.848884892549876 + }, + "lastCommittedPoint": null, + "startArrowhead": "triangle", + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 55.635925292968864, + 10.40491739908839 + ] + ] + }, + { + "type": "arrow", + "version": 374, + "versionNonce": 1700315691, + "isDeleted": false, + "id": "hD96cqfCMh9_DbP9i7uY8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 468.39591405623116, + "y": 1424.3040539884673, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 15.20001220703125, + "height": 46.4000244140625, + "seed": 2021565843, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897743207, + "link": null, + "locked": false, + "startBinding": { + "elementId": "MvzrG9xDr5qX2f2T1mybp", + "focus": -0.1456171397377326, + "gap": 1.6672744468060472 + }, + "endBinding": { + "elementId": "nQ-rdtFel6u5plaJGUHOQ", + "focus": -0.08950429577282927, + "gap": 6.282550970303841 + }, + "lastCommittedPoint": null, + "startArrowhead": "triangle", + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 15.20001220703125, + -46.4000244140625 + ] + ] + }, + { + "type": "line", + "version": 189, + "versionNonce": 327582053, + "isDeleted": false, + "id": "wA_-MayAFaVXf3aS1czx2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 511.5959262632624, + "y": 1324.3040539884673, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 37.5999755859375, + "height": 25.60003662109375, + "seed": 410803229, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897743207, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 37.5999755859375, + -25.60003662109375 + ] + ] + }, + { + "type": "line", + "version": 201, + "versionNonce": 791180139, + "isDeleted": false, + "id": "uol1dYqHO8tiNUpqVlj1S", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 577.9958896421687, + "y": 1306.7040173673736, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 9.60003662109375, + "height": 52.800048828125, + "seed": 942929245, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897743207, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 9.60003662109375, + 52.800048828125 + ] + ] + }, + { + "type": "line", + "version": 190, + "versionNonce": 207293637, + "isDeleted": false, + "id": "0vRW-a5MzT0cJ76BzBM9S", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 514.7958774351374, + "y": 1359.5040661954986, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 52, + "height": 20, + "seed": 1553728733, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897743207, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 52, + 20 + ] + ] + }, + { + "id": "QQ8clhDm-jWLAIt5gd8cO", + "type": "ellipse", + "x": 676.4073265974723, + "y": 1051.622449591054, + "width": 258.62888894457205, + "height": 443.671875, + "angle": 6.149953883166017, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "roundness": { + "type": 2 + }, + "seed": 1314683819, + "version": 159, + "versionNonce": 1487832555, + "isDeleted": false, + "boundElements": [ + { + "id": "9Qj79ByzVAy4UK13zWQzA", + "type": "arrow" + } + ], + "updated": 1681897828268, + "link": null, + "locked": false + }, + { + "id": "9Qj79ByzVAy4UK13zWQzA", + "type": "arrow", + "x": 627.1666666666666, + "y": 1378.375, + "width": 57.2265625, + "height": 19.752604166666515, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "roundness": { + "type": 2 + }, + "seed": 724737995, + "version": 28, + "versionNonce": 98226597, + "isDeleted": false, + "boundElements": null, + "updated": 1681897834436, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 57.2265625, + -19.752604166666515 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "4eSku6RRpRkssxtPyVQ8y", + "focus": 0.11616525694191442, + "gap": 8.787221477575649 + }, + "endBinding": { + "elementId": "QQ8clhDm-jWLAIt5gd8cO", + "focus": -0.1868760277026342, + "gap": 8.38772547436949 + }, + "startArrowhead": "triangle", + "endArrowhead": "triangle" + }, + { + "id": "m0I2Urq_BKU4y8wJj4BOt", + "type": "rectangle", + "x": 85.02473958333331, + "y": 1220.3867187500002, + "width": 182.85807291666663, + "height": 239.51822916666674, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "roundness": { + "type": 3 + }, + "seed": 1760776587, + "version": 203, + "versionNonce": 692259371, + "isDeleted": false, + "boundElements": null, + "updated": 1681897983834, + "link": null, + "locked": false + }, + { + "id": "K2E7BdcwFt3MNdiAe5F5f", + "type": "ellipse", + "x": 103.74489220071112, + "y": 1236.71484375, + "width": 38.860677083333314, + "height": 40.30598958333326, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "roundness": { + "type": 2 + }, + "seed": 331070539, + "version": 77, + "versionNonce": 1206534661, + "isDeleted": false, + "boundElements": null, + "updated": 1681897983834, + "link": null, + "locked": false + }, + { + "id": "gMhWUIyxn0FzRZx9mUlNH", + "type": "diamond", + "x": 102.42653282571112, + "y": 1310.1328125, + "width": 41.497395833333314, + "height": 41.640625, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "roundness": { + "type": 2 + }, + "seed": 698969995, + "version": 84, + "versionNonce": 878087371, + "isDeleted": false, + "boundElements": null, + "updated": 1681897983834, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 461, + "versionNonce": 1270724965, + "isDeleted": false, + "id": "wVtHF-ReABf_m3qlfpoMb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 92.55727640787767, + "y": 1405.4619569487415, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 21.807885422668466, + "height": 22.439988258557825, + "seed": 585061523, + "groupIds": [ + "zvuUCyZSIsR398gqihw_R" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897983834, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 494, + "versionNonce": 589347691, + "isDeleted": false, + "id": "icEr5HdxyoQBFRj5kKa7T", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 123.84681287818549, + "y": 1379.940388997396, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 21.807885422668466, + "height": 22.439988258557825, + "seed": 1365603507, + "groupIds": [ + "zvuUCyZSIsR398gqihw_R" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897983834, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 478, + "versionNonce": 686507205, + "isDeleted": false, + "id": "5tSvqxSbtOzwLk2Y5jwO6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 131.9852996542094, + "y": 1422.3709248273797, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 21.807885422668466, + "height": 22.439988258557825, + "seed": 276503091, + "groupIds": [ + "zvuUCyZSIsR398gqihw_R" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897983834, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 352, + "versionNonce": 569120267, + "isDeleted": false, + "id": "vAknLjbUPrN7jAMzclZAS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 111.20459942474426, + "y": 1408.7805330069268, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 14.854633661997813, + "height": 10.11381416647253, + "seed": 410803229, + "groupIds": [ + "zvuUCyZSIsR398gqihw_R" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897983834, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 14.854633661997813, + -10.11381416647253 + ] + ] + }, + { + "type": "line", + "version": 364, + "versionNonce": 1393583141, + "isDeleted": false, + "id": "z_f5xIxtHdX2iFNoGgOIB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 137.43725292499337, + "y": 1401.8272812462562, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 3.792689354868774, + "height": 20.859731168834422, + "seed": 942929245, + "groupIds": [ + "zvuUCyZSIsR398gqihw_R" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897983834, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 3.792689354868774, + 20.859731168834422 + ] + ] + }, + { + "type": "line", + "version": 353, + "versionNonce": 1874009259, + "isDeleted": false, + "id": "LicIyTOqfTAQzfmrSr0AQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 112.46880509652291, + "y": 1422.6870124150907, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 20.543655637712206, + "height": 7.901406014504695, + "seed": 1553728733, + "groupIds": [ + "zvuUCyZSIsR398gqihw_R" + ], + "roundness": null, + "boundElements": [], + "updated": 1681897983834, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 20.543655637712206, + 7.901406014504695 + ] + ] + }, + { + "id": "HTevBXEWUQjtp19FVQyLP", + "type": "text", + "x": 158.6640625000001, + "y": 1241.51953125, + "width": 101.1328125, + "height": 23, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "roundness": null, + "seed": 596935915, + "version": 89, + "versionNonce": 1847236485, + "isDeleted": false, + "boundElements": null, + "updated": 1681897983834, + "link": null, + "locked": false, + "text": "Community", + "fontSize": 20, + "fontFamily": 2, + "textAlign": "left", + "verticalAlign": "top", + "baseline": 18, + "containerId": null, + "originalText": "Community", + "lineHeight": 1.15 + }, + { + "type": "text", + "version": 126, + "versionNonce": 136335179, + "isDeleted": false, + "id": "g5uYQiCV8XQCrPDKA8Ny2", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 163.62500000000006, + "y": 1320.240885416667, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 52.265625, + "height": 23, + "seed": 596935915, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897983834, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Agent", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Agent", + "lineHeight": 1.15, + "baseline": 18 + }, + { + "type": "text", + "version": 180, + "versionNonce": 1145528037, + "isDeleted": false, + "id": "cgkKpJlHsTgwKwTGmXoue", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 166.52213541666669, + "y": 1388.1054687500002, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 63.349609375, + "height": 46, + "seed": 596935915, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1681897983834, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 2, + "text": "Server\nCluster", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Server\nCluster", + "lineHeight": 1.15, + "baseline": 41 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/foreword.md b/docs/foreword.md index 2317a5b..31e2bc1 100644 --- a/docs/foreword.md +++ b/docs/foreword.md @@ -1,7 +1,7 @@ # Foreword ## What is PEAK and why it was developed? -PEAK is a framework for developing Multi-Agent Systems (MAS). But you may ask, why another one? Well, the fact is that there is very few frameworks in Python and most of them are for Agent Based Modeling, which are not the same thing. The most used MAS framework is Jade, but the problem with it is that is written in java. Nowadays Python is having a lot of attention when it comes to artificial intelligence algorithms, and unfortunately java is not seeing much improvement in this area. In Python, there are a lot of AI resources out there that can be easily implemented in projects. So we search for MAS frameworks in Python and the only one that filled most of or needs was SPADE. But SPADE lacks some functionalities that we think would be great to see implemented, that is why we developed PEAK. Instead of reinventing the wheel we used SPADE too build a more sophisticated MAS framework that is inspired by others frameworks, like Jade. +PEAK is a framework for developing Multi-Agent Systems (MAS). But you may ask, why another one? Well, the fact is that there are very few frameworks in Python and most of them are for Agent Based Modeling, which is not the same thing. The most used MAS framework is Jade, but the problem is that it is written in java. Nowadays, Python is having a lot of attention when it comes to artificial intelligence algorithms, and unfortunately java is not seeing much improvement in this area. In Python, there are a lot of AI resources out there that can be easily implemented in projects. So we search for MAS frameworks in Python and the only one that filled most of or needs was SPADE. But SPADE lacks some functionalities that we think would be great to see implemented, that is why we developed PEAK. Instead of reinventing the wheel, we used SPADE to build a more sophisticated MAS framework that is inspired by others frameworks, like Jade. ## Recommendations before using PEAK Before continuing with PEAK we highly recommend you too go through the SPADE's docs and tutorials so you can be accustomed with SPADE's workflow. Every SPADE's functionality is available in PEAK. PEAK's how to guide only shows the features that are new in the PEAK framework. You only need to know to to create an Agent, how to create and execute Behavior's and how to communicate between agents. \ No newline at end of file diff --git a/docs/how-to.md b/docs/how-to.md index 034d2a1..c3e42c8 100644 --- a/docs/how-to.md +++ b/docs/how-to.md @@ -1,9 +1,7 @@ # How-to Guides +This section will going to guide you through PEAK's fundamental ideas and functionalities. Every example given here is available in the [repository](https://github.com/gecad-group/peak-mas). -In this section you will going through PEAK's functionalities. Every example given here is available in PEAK's repository. - -## Run and configure a MAS - +## PEAK Basic principles You have two options to run PEAK agents. You can run the same way as in SPADE by using a python script or using PEAK's command line interface (CLI). > **Note:** @@ -16,18 +14,16 @@ You have two options to run PEAK agents. You can run the same way as in SPADE by > positional arguments: > {df,run,start} > df Execute Directory Facilitator agent. -> run Execute PEAK's agents. +> run Execute PEAK agents. > start Executes multiple agents using a YAML configuration > file. > >optional arguments: > -h, --help show this help message and exit -> -version show program's version number and exit -> -v Verbose. Turns on the debug info. +> --version show programs version number and exit > ``` -### Run a single agent - +### How to create and Agent ([Example 1](https://github.com/gecad-group/peak-mas/tree/main/examples/1_simple_agent)) Let's run a simple agent. ```python @@ -61,7 +57,7 @@ The JID is the ID used in the XMPP protocol and is divided by three parts: the l After you run the agent you will see a new folder appear in the same directory of the `agent.py`. That folder is the `logs` folder and will contain the log files created for each agent you run. You can change the logging level for each agent using the command line argument `-l`. This files come in handy when running complex systems with lots of behaviors. You can track everything the agent does and when it does with the logging functionality. -### Run multiple agents +### Communication between Agents ([Example 2](https://github.com/gecad-group/peak-mas/tree/main/examples/2_multiagent_system)) To run multiple agents at the same time you can use a configuration file in YAML format. ```python @@ -70,13 +66,13 @@ from peak import Agent, OneShotBehaviour, Message class sender(Agent): class SendHelloWorld(OneShotBehaviour): - async def run(self) -> None: + async def run(self): msg = Message(to="harry@localhost") msg.body = "Hello World" await self.send(msg) await self.agent.stop() - async def setup(self) -> None: + async def setup(self): self.add_behaviour(self.SendHelloWorld()) ``` @@ -86,39 +82,45 @@ from peak import Agent, OneShotBehaviour class receiver(Agent): class ReceiveHelloWorld(OneShotBehaviour): - async def run(self) -> None: + async def run(self): while msg := await self.receive(10): print(f"{msg.sender} sent me a message: '{msg.body}'") await self.agent.stop() - async def setup(self) -> None: + async def setup(self): self.add_behaviour(self.ReceiveHelloWorld()) ``` ```yaml -# multiagent.yaml +# mas.yaml defaults: domain: localhost - log_level: debug agents: john: file: sender.py harry: file: receiver.py - log_level: info ``` -Let's create two agents one that sends the a message, the `sender.py`, and one that receives the message, the `receiver.py`. In the same directory create the YAML file above with the name `multiagent.yaml`. After that, run the following command: +Let's create two agents one that sends the a message, the `sender.py`, and one that receives the message, the `receiver.py`. In the same directory create the YAML file above with the name `mas.yaml`. After that, run the following command: ```bash -$ peak start multiagent.yaml +$ peak start mas.yaml ``` +<<<<<<< HEAD +So, what happened? Two agents were created. One called `john@localhost` and the other called `harry@localhost`. `john` sent a `Hello World` to `harry` and `harry` printed it out. + +The way it works is simple. You can only define two root variables, the `defaults` and the `agents`. The `defaults` is used to define parameters to be applied to all agents. The `agents` variable defines the list of agents to be executed and their individual parameters. Type `peak start -h` on the terminal to see the list of available parameters. + +In this case we are defining, in the `defaults`, the default domain as `localhost` for all agents. In `agents` variable, we are defining two different types of agents, the `john` and the `harry`. In both agents we are defining their source file. The `agents` parameters will override the `defaults` parameters if they are the same. +======= So, what happened? Two agents were created. One called `john@localhost` and the other called `harry@localhost`. `john` sent a message `Hello World` to `harry` and `harry` printed it out. The log file of `jonh` was in logging level `DEBUG`, and `harry`'s file was in level `INFO`. The way it works is simple. You can only define two root variables, the `defaults` and the `agents`. The `defaults` is used to define parameters to be applied to all agents. The `agents` variable defines the list of agents to be executed and their respective parameters. The parameters available in `defaults` and in the agents of the variable `agents` can be seen using the `-h` argument in the `peak run` command. In this case we are defining, in the `defaults`, the default domain as `localhost` and the default logging level as `debug` for all agents. In `agents` variable, we are defining two different types of agents, the `john` and the `harry`. In `john` we are defining the agents source file. In `harry` we are defining the source file and the logging level, overriding the default value. +>>>>>>> main There is the list of options that you can define in the configuration file, inside each agent and in the `defaults` variable: - `file` - source file of the agent @@ -126,142 +128,189 @@ There is the list of options that you can define in the configuration file, insi - `resource` - resource to be used in the JID - `log_level` - logging level of the log file - `clones` - number of clones to be executed +<<<<<<< HEAD +======= - `properties` - source file of the agent's properties (more on that later) +>>>>>>> main - `verify_security` - if present verifies the SSL certificates ### Thread vs. Process _In development_ +This section will talk about how to run agents as different threads of the same process or as multiple processes and the benefits and use cases for each approach. + +## PEAK Communities + +<<<<<<< HEAD +In PEAK, communities can be seen as groups of agents that share similar goals. Communities are a very useful and efficient way to make communication between three or more agents. What makes this usefull is that for each message sent to the community every member will receive the message. + + -## Create a group of agents +For this examples you will need to execute a pre-defined PEAK agent called Directory Facilitator, for short DF. He is responsable to maintain the PEAK communities structures. More details about the DF [here](). +### Creating a community ([Example 3](https://github.com/gecad-group/peak-mas/tree/main/examples/3_simple_community)) + +To create a community is very simple. There is a pre defined behavior that enables the agent join communities. For only this functionality you don't need DF, but it is recommended. +======= The groups are a very useful way to make the communication between more than two agents. To create a group is very simple. There is a pre defined behavior that enables the agent to create and join groups. +>>>>>>> main ```python #agent.py -from peak import Agent, JoinGroup, OneShotBehaviour, Message from asyncio import sleep +from peak import Agent, JoinCommunity, LeaveCommunity, Message, OneShotBehaviour + + class agent(Agent): class HelloWorld(OneShotBehaviour): +<<<<<<< HEAD + async def on_start(self): + await self.wait_for(JoinCommunity("group1", f"conference.{self.agent.jid.domain}")) +======= async def on_start(self) -> None: await self.wait_for(JoinGroup("group1", f"conference.{self.agent.jid.domain}")) +>>>>>>> main - async def run(self) -> None: + async def run(self): msg = Message(to=f"group1@conference.{self.agent.jid.domain}") msg.body = "Hello World" - await self.send_to_group(msg) + await self.send_to_community(msg) await sleep(5) msg.body = "Goodbye World" - await self.send_to_group(msg) + await self.send_to_community(msg) + self.kill() + + async def on_end(self): + await self.wait_for(LeaveCommunity("group1", f"conference.{self.agent.jid.domain}")) await self.agent.stop() - async def setup(self) -> None: + async def setup(self): self.add_behaviour(self.HelloWorld()) ``` -As you can see in the example above, the agent has a behavior `HelloWorld`. This behavior will first use the `JoinGroup` behavior to join a group called 'group1@conference.localhost' (assuming the domain is ``localhost``). If the group does not exists it will create it. It waits until the agent joins the group. After that it will run the `run` function. The ``run`` function will send a `Hello World` message to the group, waits for 5 seconds and then sends a `Goodbye World` and exits. If you want to leave the group without terminating the agent you can import the ``LeaveGroup`` behavior from ``peak`` package and use it the same way as the `JoinGroup`. +Then run the following command: +```bash +$ peak run agent.py -j dummy@localhost +``` +As you can see in the example above, the agent has a behavior `HelloWorld`. This behavior will first use the `JoinCommunity` behavior to join a community called `group1@conference.localhost`. If the community does not exists it will create it automatically. It waits until the agent joins the community. After that it will send a `Hello World` message to the community, waits for 5 seconds and then sends a `Goodbye World` and leaves the community with the `LeaveCommunity` behaviour. > **Note:** -> For the this functionality to work the server must have Multi-User Chat functionality activated. You need to create a component in the server and give it a prefix, in this case is 'conference'. +> For the this functionality to work the server must have Multi-User Chat functionality activated. You need to create a component in the server and give it a prefix, in this case is 'conference'. See [this](xmpp_config.md) server configuration file example. > **Tip:** > You can see the messages being sent if you use the XMPP client and enter in the same room as the agent. Is a good way to debug the multi-agent. -### Group tagging +> **Chalenge 1:** +> Try to implement the [Communication between agents](#communication-between-agents-example-2) example using communities. ([Solution]()) + +### Community tagging ([Example 4](https://github.com/gecad-group/peak-mas/tree/main/examples/4_community_tags)) -Group tagging, as the name suggests, is for tagging the groups. This allows the agents to identify the groups and then search for them using the tags to filter the groups. Let's see. +Community tagging, as the name suggests, is for tagging communities. This allows the agents to identify the communities and then search for them using the tags to filter them. Let's see. ```python -#group_searcher.py -from peak import Agent, JoinGroup, SearchGroup -import logging -logger = logging.getLogger(self.__class__.__name__) +#agent.py +from peak import Agent, JoinCommunity, LeaveCommunity, OneShotBehaviour, SearchCommunity -class group_searcher(Agent): - async def setup(self) -> None: - self.add_behaviour( - JoinGroup("group1", "conference.localhost", ["test", "awesome", "cool"]) - ) - self.add_behaviour( - JoinGroup("group2", "conference.localhost", ["test", "awesome"]) - ) - self.add_behaviour( - JoinGroup("group3", "conference.localhost", ["test"],) - ) - self.add_behaviour(JoinGroup("group4", "conference.localhost")) - - def print_groups(tags, groups): - logger.info(str(tags), str(groups)) + +class agent(Agent): + class TagCommunities(OneShotBehaviour): + async def run(self) -> None: + self.agent.add_behaviour( + JoinCommunity( + "group1", + f"conference.{self.agent.jid.domain}", + ["test", "awesome"], + ) + ) + self.agent.add_behaviour( + JoinCommunity( + "group2", f"conference.{self.agent.jid.domain}", ["test"] + ) + ) + def print_communities(tags, communities): + print(f"Communities ({tags}): {communities}") + + await self.wait_for(SearchCommunity(["awesome"], print_communities)) + await self.wait_for(SearchCommunity(["test"], print_communities)) - self.add_behaviour(SearchGroup(["test"], print_groups)) - self.add_behaviour(SearchGroup(["awesome"], print_groups)) - self.add_behaviour(SearchGroup(["awesome", "cool"], print_groups)) + await self.wait_for( + LeaveCommunity("group1", f"conference.{self.agent.jid.domain}") + ) + await self.wait_for( + LeaveCommunity("group2", f"conference.{self.agent.jid.domain}") + ) + await self.agent.stop() + + async def setup(self) -> None: + self.add_behaviour(self.TagCommunities()) ``` -In the example above we create an agent called `group_searcher`. This agent will create 4 different groups: `group1@conference.localhost` with the tags `test`, `awesome` and `cool`; `group2@conference.localhost` with the tags `test` and `awesome`; `group3@conference.localhost` with the tag `test`; and finally the fourth group `group4@conference.localhost`. After that will search for groups using the `SearchGroup` behavior. -Firstly, the `JoinGroup` is used to create the groups and tag them. The groups can have more than one tag. To search the groups it's used the `SearchGroup` behavior. You can search for more than one tag, but be careful because it used conjugation to search for them. In other words it will get you the list of groups that have all the tags the you mentioned. +For this to work you need to execute the DF agent (more details [here]()). In the example above we create an agent that will create and tag two communities: `group1@conference.localhost` with tags `test` and `awesome`, and `group2@conference.localhost` with tag `test`. After that will search for communities using the `SearchCommunity` behavior. Note that for the `SearchCommunity` and for `LeaveCommunity` behaviours we do not add the behaviour directly to the agent, because the agent would stop before the behaviours would be accomplished. We use `wait_for` method, available in every behaviour, to wait for the completeness of the behaviour before continuing, making the behaviour synchrounous. -### Group Hierarchy +In simple words the tagging system will search for the communities that have the tags that you want. For instance, if you search for three tags it will give you every community that have all those three tags. -The group hierarchy allows the user to create subgroups in different depths. This does not only allow you to organize your multiagent system (e.g., society of agent) but also to communicate more efficiently through the hierarchical branches. You can send a single message to every agent that is in a specific branch, or every agent bellow some specific node. +> **Chalenge 2:** +> Try to implement this example with two agents: one that creates and tags the communities and other that searches for them. ([Solution](https://github.com/gecad-group/peak-mas/tree/main/examples/chalenge_2)) -For this functionality you need to activate the Directory Facilitator (DF) agent. DF coordinates and monitors the hierarchical structure. To activate it you just need to run the following command: +### Community hierarchy ([Example 5](https://github.com/gecad-group/peak-mas/tree/main/examples/5_community_hierarchy)) -```bash -$ peak df -``` +The community hierarchy allows the user to create different levels of communities associated with one another. This does not only allow your agents to organize themselves but also to communicate more efficiently through the hierarchical branches. For instance, consider the hirarchy bellow. + +_Image_ + +For every community of the hierarchy two nodes are created: the actual node of the community, which only the members can interact, and a node which has all the members of that community all the way down to the roots. This special node that we call the `echo node` will work has an echoer for the whole branch beneaf. In other words, if you want to send a message from X to Y you just need to send a message to the X_down node. + +Let's see an example of the community hierarchy. -To change the server domain use the `-d` argument. -Let's see an example of the group hierarchy. ```python #agent.py +# Standard library imports from asyncio import sleep -from peak import Agent, JoinGroup, LeaveGroup, Message, OneShotBehaviour + +# Reader imports +from peak import Agent, JoinCommunity, LeaveCommunity, Message, OneShotBehaviour + class agent(Agent): class HelloWorld(OneShotBehaviour): async def run(self): - self.agent.add_behaviour( - JoinGroup("mas/retina/teste", f"conference.{self.agent.jid.domain}") - ) - msg = Message( - to=f"retina@conference.{self.agent.jid.domain}" - ) - msg.body = "Hello World" - await self.send_to_group(msg) + groups_tree = [ + "peak/A0/B0", + "peak/A0", + "peak/A1", + "peak/A2/B2/C0", + "peak/A1/B3/C1", + ] + for groups_branch in groups_tree: + await self.wait_for( + JoinCommunity(groups_branch, "conference." + self.agent.jid.domain) + ) + await sleep(1) + for groups_branch in groups_tree: + await self.wait_for( + LeaveCommunity(groups_branch, "conference." + self.agent.jid.domain) + ) + await sleep(1) + await self.agent.stop() async def setup(self): self.add_behaviour(self.HelloWorld()) ``` -In the JoinGroup behavior you can specify a path of groups. What these does is create an hierarchy of groups, being the first group the root and the last group an intermediate node or a leaf. In this path we call the last node the target group, because you only enter in that group specifically. Another interesting thing about this functionality is that for each node behind the target node you enter in a specially group which the names ends in \_down. The ideia behind this is to send messages to the whole branch, so every agent that enters a node bellow enters in this group. So if we want to send a message to the whole multi agent system we only have to send a message to, in the case of this example, ``mas_down``. In the example above the agent entered in +For this to work you need to execute the DF agent (more details [here]()). In the `JoinCommunity` behavior you can specify a path representing the hierarchy of the communities. For instance in path `peak/A0/B0`, `peak` is the root community, beneath is community `A0`, at level 1, and `B0` at level 2. The last node of the path does not need necessarilly to be a leaf. We call the last node the target onde, because you only enter in that community specifically. > **Note:** > In the Dashboard, the groups that end in \_down do not appear in the node graph. -## Create a simulation environment +## Simulation Environment ### Clock ### Dynamic clock _In development_ -## Integrate data providers - -Normally a multi-agent system uses external data so it can process that data. In PEAK you can integrate different data providers like files from the file system of your machine, databases in the web and from real devices using some real-time communication protocol. - -### File System -- Excel -- CSV - -### Smart devices -- ModBus/TCP -- HTTPs -- Extending - ## PEAK Dashboard - The PEAK Dashboard is a separate project from PEAK. The Dashboard allows you to see in an interactive way the PEAK ecosystem using a web app. The Dashboard needs a Directory Facilitator (DF) agent in the XMPP server so it can monitor the system. The Dashboard communicates with the DF through its REST API. To know how to activate the DF run the following command: ```bash $ peak df -h ``` -### Ecosystem Insight +### Ecosystem Overview ### Data Analysis ### Full Plot Customization \ No newline at end of file diff --git a/docs/installation.md b/docs/installation.md index af4bddb..e6c2ac8 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,13 +1,18 @@ # Installing PEAK Here we are going to teach you how to install PEAK. -To install PEAK with [pip](https://pip.pypa.io/en/stable/) just type this in the terminal: +Firstly, you need to install Python v3.9.6. After that, you can install PEAK by using [pip](https://pip.pypa.io/en/stable/) command like this: ```bash $ pip install peak-mas ``` -To install via [conda](https://docs.conda.io/en/latest/miniconda.html) you need to download the environment.yml file available in the repository, and then just type this following commands: +Another way to install PEAK is via [conda](https://docs.conda.io/en/latest/miniconda.html). For that, you can download the env.yml file from the repository, and type these commands in the terminal: ```bash $ conda env create -f environment.yml $ conda activate peak ``` -Now your ready to go! \ No newline at end of file +Test if it is well installed by using one of the commands bellow: +```bash +$ peak +$ python -m peak +``` +Now you are ready to go! \ No newline at end of file diff --git a/docs/peak_communities.png b/docs/peak_communities.png new file mode 100644 index 0000000000000000000000000000000000000000..e9442389dd97dbb0c17ed5ff5b356a97de8b9b0b GIT binary patch literal 241981 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

Ma<1*}LGrb^fzk{%@rkmJ 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

8heXakeS0%A3weJs!1 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 literal 0 HcmV?d00001 diff --git a/docs/prerequistites.md b/docs/prerequistites.md index cc4d050..4af94e2 100644 --- a/docs/prerequistites.md +++ b/docs/prerequistites.md @@ -1,19 +1,16 @@ # Prerequisites - To start using PEAK you will need a XMPP server. XMPP stands for Extensible Messaging and Presence Protocol. This server is what will enable us to create the agents and make them communicate. XMPP is a protocol used for real time chatting so it's a good protocol for agents. You can either create and configure a local server or use a public one. The downside of using a public one is that some features of PEAK will not work (e.g. auto registration, multi-user chat, discovery service) depending on the server configuration. Another option is to use a docker, specially if you are on a Windows machine. We recommend the use of Prosody XMPP server because it's more compatible. PEAK was tested under Windows and Linux. To use PEAK you can either run in a local XMPP server configured by you, or run in a Docker with the XMPP server inside. ## Run in a XMPP server +To run and configure a local Prosody server you will need a Linux operating system. To create a local server just go to [Prosody's docs](https://prosody.im/download/start) and follow the instructions. After creating the server head to [XMPP server configurations](#xmpp-server-configurations). +Prosody doesn't support Windows natively. If you have Windows and want to install locally you can either chose to run in a [Docker (see next section)](#run-in-a-docker), use Windows Subsystem for Linux (WSL) or use other [server providers](https://xmpp.org/software/servers/). Other servers providers may not be compatible with PEAK, but feel free to post in the [discussion forum](https://github.com/gecad-group/peak-mas/discussions) any problem you might find. -To run and configure a local Prosody server you will need a Linux operating system. Prosody doesn't support Windows natively. If you have Windows and want to install locally anyway you can either chose to run in a [Docker](#run-in-a-docker), using Windows Subsystem for Linux or using other [server providers](https://xmpp.org/software/servers/). Bare in mind that PEAK is tested in Prosody server, but feel free to post a discussion about having problem with other servers in the [Discussion section](https://github.com/gecad-group/peak-mas/discussions) at our repository. -To create a local server just go to [Prosody's docs](https://prosody.im/download/start) and follow the instructions. After creating the server head to [XMPP server configurations](#xmpp-server-configurations). ## Run in a Docker - There are some dockers available in Docker Hub. In the future we will make or own docker with the ability to run the server and the agents in the same docker. For now you can use [Prosody's official docker](https://github.com/prosody/prosody-docker). ## XMPP server configurations - To be able to use every functionality in PEAK you need specific configurations on the server. Bellow is the configuration file that you can use to configure the server. This file only has the essentials to run PEAK features. For any additional configurations you can head to [Prosody's documentation](https://prosody.im/doc/configure). If you are using other server than Prosody and want to know which functionalities you have to enable se the following list (its probable that you can not tweak or find every option in other servers): @@ -24,128 +21,12 @@ If you are using other server than Prosody and want to know which functionalitie - de-enable room locking - change the max history messages to zero +See the example configuration file [here](xmpp_config.md). + > **Note:** > If you want to save messages logs in the server for debugging proposes use the `mam` and `message_logging` modules. Bare in mind that this will slow down your server, especially if you have high load on the server. -```lua --- config.lua - --- Prosody Configuration File for PEAK - ----------- Server-wide settings ---------- - --- Settings in this section apply to the whole server and are the default settings --- for any virtual hosts - - --- This is the list of admins. --- DF agent must be admin for some features to work. -admins = {"df@localhost"} - --- Enable use of libevent for better performance under high load -use_libevent = true - --- Paths for community models: -plugin_paths = { - "/usr/local/lib/prosody/modules"; - "/home/happy_dori/Documentos/prosody-modules"; - } - --- This is the list of modules Prosody will load on startup. --- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too. -modules_enabled = { - -- Generally required - "roster"; -- Allow users to have a roster. Recommended ;) - "saslauth"; -- Authentication for clients and servers. Recommended if you want to log in. - "tls"; -- Add support for secure TLS on c2s/s2s connections - "dialback"; -- s2s dialback support - "disco"; -- Service discovery - - -- Not essential, but recommended - "carbons"; -- Keep multiple clients in sync - "pep"; -- Enables users to publish their avatar, mood, activity, playing music and more - "private"; -- Private XML storage (for room bookmarks, etc.) - "blocklist"; -- Allow users to block communications with other users - "vcard4"; -- User profiles (stored in PEP) - "vcard_legacy"; -- Conversion between legacy vCard and PEP Avatar, vcard - - -- Nice to have - "version"; -- Replies to server version requests - "uptime"; -- Report how long server has been running - "time"; -- Let others know the time here on this server - "ping"; -- Replies to XMPP pings with pongs - "register"; -- Allow users to register on this server using a client and change passwords - -- "mam"; -- Store messages in an archive and allow users to access it - -- "message_logging"; -- Log/archive all user messages - - -- Admin interfaces - "admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands - - -- Other specific functionality - "posix"; -- POSIX functionality, sends server to background, enables syslog, etc. -} - --- These modules are auto-loaded, but should you want --- to disable them then uncomment them here: -modules_disabled = { - "s2s"; -- Handle server-to-server connections -} - --- Allows agents to register freely -allow_registration = true - -daemonize = false; -pidfile = "/run/prosody/prosody.pid"; -c2s_require_encryption = true -network_backend = "epoll" - --- Select the authentication backend to use. The 'internal' providers --- use Prosody's configured data storage to store the authentication data. -authentication = "internal_hashed" - --- Logging configuration --- For advanced logging see https://prosody.im/doc/logging --- Debian: ---  Logs info and higher to /var/log ---  Logs errors to syslog also - -log = { - -- Log files (change 'info' to 'debug' for debug logs): - info = "/var/log/prosody/prosody.log"; - error = "/var/log/prosody/prosody.err"; - -- Syslog: - { levels = { "error" }; to = "syslog";  }; -} - -certificates = "certs" - - ------------ Virtual hosts ----------- - -VirtualHost "localhost" - ------- Components ------ - --- You can specify components to add hosts that provide special services, --- like multi-user conferences, and transports. --- For more information on components, see https://prosody.im/doc/components - ----Set up a MUC (multi-user chat) room server on conference.example.com: - -Component "conference.localhost" "muc" - muc_room_default_public = true - muc_room_default_public_jids = true - muc_room_locking = false - max_history_messages = 0 - -Component "pubsub.localhost" "pubsub" - autocreate_on_subscribe = true - autocreate_on_publish = true - -Include "conf.d/*.cfg.lua" -``` - ## XMPP Client This is optional, but one good way to debug PEAK, additionally to PEAK's debug system, is having a XMPP client to see the messages exchange between agents. There are plenty of [clients](https://xmpp.org/software/clients/) you can use. The one used to develop PEAK is [Pidgin](https://www.pidgin.im/). \ No newline at end of file diff --git a/docs/xmpp_config.md b/docs/xmpp_config.md new file mode 100644 index 0000000..73a3306 --- /dev/null +++ b/docs/xmpp_config.md @@ -0,0 +1,117 @@ +```lua +-- config.lua + +-- Prosody Configuration File for PEAK + +---------- Server-wide settings ---------- + +-- Settings in this section apply to the whole server and are the default settings +-- for any virtual hosts + + +-- This is the list of admins. +-- DF agent must be admin for some features to work. +admins = {"df@localhost"} + +-- Enable use of libevent for better performance under high load +use_libevent = true + +-- Paths for community models: +plugin_paths = { + "/usr/local/lib/prosody/modules"; + "/home/happy_dori/Documentos/prosody-modules"; + } + +-- This is the list of modules Prosody will load on startup. +-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too. +modules_enabled = { + -- Generally required + "roster"; -- Allow users to have a roster. Recommended ;) + "saslauth"; -- Authentication for clients and servers. Recommended if you want to log in. + "tls"; -- Add support for secure TLS on c2s/s2s connections + "dialback"; -- s2s dialback support + "disco"; -- Service discovery + + -- Not essential, but recommended + "carbons"; -- Keep multiple clients in sync + "pep"; -- Enables users to publish their avatar, mood, activity, playing music and more + "private"; -- Private XML storage (for room bookmarks, etc.) + "blocklist"; -- Allow users to block communications with other users + "vcard4"; -- User profiles (stored in PEP) + "vcard_legacy"; -- Conversion between legacy vCard and PEP Avatar, vcard + + -- Nice to have + "version"; -- Replies to server version requests + "uptime"; -- Report how long server has been running + "time"; -- Let others know the time here on this server + "ping"; -- Replies to XMPP pings with pongs + "register"; -- Allow users to register on this server using a client and change passwords + -- "mam"; -- Store messages in an archive and allow users to access it + -- "message_logging"; -- Log/archive all user messages + + -- Admin interfaces + "admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands + + -- Other specific functionality + "posix"; -- POSIX functionality, sends server to background, enables syslog, etc. +} + +-- These modules are auto-loaded, but should you want +-- to disable them then uncomment them here: +modules_disabled = { + "s2s"; -- Handle server-to-server connections +} + +-- Allows agents to register freely +allow_registration = true + +daemonize = false; +pidfile = "/run/prosody/prosody.pid"; +c2s_require_encryption = true +network_backend = "epoll" + +-- Select the authentication backend to use. The 'internal' providers +-- use Prosody's configured data storage to store the authentication data. +authentication = "internal_hashed" + +-- Logging configuration +-- For advanced logging see https://prosody.im/doc/logging +-- Debian: +--  Logs info and higher to /var/log +--  Logs errors to syslog also + +log = { + -- Log files (change 'info' to 'debug' for debug logs): + info = "/var/log/prosody/prosody.log"; + error = "/var/log/prosody/prosody.err"; + -- Syslog: + { levels = { "error" }; to = "syslog";  }; +} + +certificates = "certs" + + +----------- Virtual hosts ----------- + +VirtualHost "localhost" + +------ Components ------ + +-- You can specify components to add hosts that provide special services, +-- like multi-user conferences, and transports. +-- For more information on components, see https://prosody.im/doc/components + +---Set up a MUC (multi-user chat) room server on conference.example.com: + +Component "conference.localhost" "muc" + muc_room_default_public = true + muc_room_default_public_jids = true + muc_room_locking = false + max_history_messages = 0 + +Component "pubsub.localhost" "pubsub" + autocreate_on_subscribe = true + autocreate_on_publish = true + +Include "conf.d/*.cfg.lua" +``` \ No newline at end of file diff --git a/examples/hello_world_agent/agent.py b/examples/1_simple_agent/agent.py similarity index 51% rename from examples/hello_world_agent/agent.py rename to examples/1_simple_agent/agent.py index 93d53bc..4749119 100644 --- a/examples/hello_world_agent/agent.py +++ b/examples/1_simple_agent/agent.py @@ -1,13 +1,11 @@ -# Reader imports -from peak import Agent -from peak.behaviours import Message, OneShotBehaviour +from peak import Agent, OneShotBehaviour class agent(Agent): class HelloWorld(OneShotBehaviour): - async def run(self) -> None: + async def run(self): print("Hello World") await self.agent.stop() - async def setup(self) -> None: + async def setup(self): self.add_behaviour(self.HelloWorld()) diff --git a/examples/2_multiagent_system/mas.yaml b/examples/2_multiagent_system/mas.yaml new file mode 100644 index 0000000..2e9801f --- /dev/null +++ b/examples/2_multiagent_system/mas.yaml @@ -0,0 +1,7 @@ +defaults: + domain: localhost +agents: + john: + file: sender.py + harry: + file: receiver.py \ No newline at end of file diff --git a/examples/2_multiagent_system/receiver.py b/examples/2_multiagent_system/receiver.py new file mode 100644 index 0000000..6f9e427 --- /dev/null +++ b/examples/2_multiagent_system/receiver.py @@ -0,0 +1,12 @@ +from peak import Agent, OneShotBehaviour + + +class receiver(Agent): + class ReceiveHelloWorld(OneShotBehaviour): + async def run(self): + while msg := await self.receive(10): + print(f"{msg.sender} sent me a message: '{msg.body}'") + await self.agent.stop() + + async def setup(self): + self.add_behaviour(self.ReceiveHelloWorld()) diff --git a/examples/2_multiagent_system/sender.py b/examples/2_multiagent_system/sender.py new file mode 100644 index 0000000..d9a2290 --- /dev/null +++ b/examples/2_multiagent_system/sender.py @@ -0,0 +1,13 @@ +from peak import Agent, Message, OneShotBehaviour + + +class sender(Agent): + class SendHelloWorld(OneShotBehaviour): + async def run(self): + msg = Message(to="harry@localhost") + msg.body = "Hello World" + await self.send(msg) + await self.agent.stop() + + async def setup(self): + self.add_behaviour(self.SendHelloWorld()) diff --git a/examples/3_simple_community/agent.py b/examples/3_simple_community/agent.py new file mode 100644 index 0000000..d8c60f9 --- /dev/null +++ b/examples/3_simple_community/agent.py @@ -0,0 +1,29 @@ +from asyncio import sleep + +from peak import Agent, JoinCommunity, LeaveCommunity, Message, OneShotBehaviour + + +class agent(Agent): + class HelloWorld(OneShotBehaviour): + async def on_start(self): + await self.wait_for( + JoinCommunity("group1", f"conference.{self.agent.jid.domain}") + ) + + async def run(self): + msg = Message(to=f"group1@conference.{self.agent.jid.domain}") + msg.body = "Hello World" + await self.send_to_community(msg) + await sleep(5) + msg.body = "Goodbye World" + await self.send_to_community(msg) + self.kill() + + async def on_end(self): + await self.wait_for( + LeaveCommunity("group1", f"conference.{self.agent.jid.domain}") + ) + await self.agent.stop() + + async def setup(self): + self.add_behaviour(self.HelloWorld()) diff --git a/examples/4_community_tags/agent.py b/examples/4_community_tags/agent.py new file mode 100644 index 0000000..d3695ef --- /dev/null +++ b/examples/4_community_tags/agent.py @@ -0,0 +1,34 @@ +# agent.py +from peak import Agent, JoinCommunity, LeaveCommunity, OneShotBehaviour, SearchCommunity + + +class agent(Agent): + class TagCommunities(OneShotBehaviour): + async def run(self) -> None: + self.agent.add_behaviour( + JoinCommunity( + "group1", + f"conference.{self.agent.jid.domain}", + ["test", "awesome"], + ) + ) + self.agent.add_behaviour( + JoinCommunity("group2", f"conference.{self.agent.jid.domain}", ["test"]) + ) + + def print_communities(tags, communities): + print(f"Communities ({tags}): {communities}") + + await self.wait_for(SearchCommunity(["awesome"], print_communities)) + await self.wait_for(SearchCommunity(["test"], print_communities)) + + await self.wait_for( + LeaveCommunity("group1", f"conference.{self.agent.jid.domain}") + ) + await self.wait_for( + LeaveCommunity("group2", f"conference.{self.agent.jid.domain}") + ) + await self.agent.stop() + + async def setup(self) -> None: + self.add_behaviour(self.TagCommunities()) diff --git a/examples/5_community_hierarchy/agent.py b/examples/5_community_hierarchy/agent.py new file mode 100644 index 0000000..39f0bd9 --- /dev/null +++ b/examples/5_community_hierarchy/agent.py @@ -0,0 +1,29 @@ +from asyncio import sleep + +from peak import Agent, JoinCommunity, LeaveCommunity, OneShotBehaviour + + +class agent(Agent): + class HelloWorld(OneShotBehaviour): + async def run(self): + groups_tree = [ + "peak/A0/B0", + "peak/A0", + "peak/A1", + "peak/A2/B2/C0", + "peak/A1/B3/C1", + ] + for groups_branch in groups_tree: + await self.wait_for( + JoinCommunity(groups_branch, "conference." + self.agent.jid.domain) + ) + await sleep(1) + for groups_branch in groups_tree: + await self.wait_for( + LeaveCommunity(groups_branch, "conference." + self.agent.jid.domain) + ) + await sleep(1) + await self.agent.stop() + + async def setup(self): + self.add_behaviour(self.HelloWorld()) diff --git a/examples/agent_groups/group_hierarchy/agent.py b/examples/agent_groups/group_hierarchy/agent.py deleted file mode 100644 index b37e426..0000000 --- a/examples/agent_groups/group_hierarchy/agent.py +++ /dev/null @@ -1,38 +0,0 @@ -# Standard library imports -from asyncio import sleep - -# Reader imports -from peak import Agent, JoinGroup, LeaveGroup, Message, OneShotBehaviour - - -class agent(Agent): - class HelloWorld(OneShotBehaviour): - async def run(self): - groups_tree = [ - "mas/retina2/teste", - "mas/retina2", - "mas/retina", - "mas/test/test2/peak", - "mas/retina/community/consumers", - ] - for _ in range(3): - for groups_branch in groups_tree: - await sleep(5) - self.agent.add_behaviour( - JoinGroup(groups_branch, "conference." + self.agent.jid.domain) - ) - group_leef = groups_branch.split("/")[-1] - msg = Message( - to=group_leef + "@conference." + self.agent.jid.domain - ) - msg.body = "Hello World" - await self.send_to_group(msg) - for groups_branch in groups_tree: - await sleep(5) - self.agent.add_behaviour( - LeaveGroup(groups_branch, "conference." + self.agent.jid.domain) - ) - await self.agent.stop() - - async def setup(self): - self.add_behaviour(self.HelloWorld()) diff --git a/examples/agent_groups/group_tags/agents.yaml b/examples/agent_groups/group_tags/agents.yaml deleted file mode 100644 index 70a2de1..0000000 --- a/examples/agent_groups/group_tags/agents.yaml +++ /dev/null @@ -1,7 +0,0 @@ -defaults: - domain: localhost -agents: - group_admin: - file: group_admin.py - group_searcher: - file: group_searcher.py \ No newline at end of file diff --git a/examples/agent_groups/group_tags/group_admin.py b/examples/agent_groups/group_tags/group_admin.py deleted file mode 100644 index 5155e7e..0000000 --- a/examples/agent_groups/group_tags/group_admin.py +++ /dev/null @@ -1,51 +0,0 @@ -# Standard library imports -from asyncio import sleep - -# Reader imports -from peak import Agent, JoinGroup, LeaveGroup, OneShotBehaviour - - -class group_admin(Agent): - class JoinGroups(OneShotBehaviour): - async def run(self) -> None: - self.agent.add_behaviour( - JoinGroup( - "group1", - f"conference.{self.agent.jid.domain}", - ["test", "awesome", "cool"], - ) - ) - self.agent.add_behaviour( - JoinGroup( - "group2", f"conference.{self.agent.jid.domain}", ["test", "awesome"] - ) - ) - self.agent.add_behaviour( - JoinGroup( - "group3", - f"conference.{self.agent.jid.domain}", - [ - "test", - ], - ) - ) - self.agent.add_behaviour( - JoinGroup("group4", f"conference.{self.agent.jid.domain}") - ) - await sleep(10) - await self.wait_for( - LeaveGroup("group1", f"conference.{self.agent.jid.domain}") - ) - await self.wait_for( - LeaveGroup("group2", f"conference.{self.agent.jid.domain}") - ) - await self.wait_for( - LeaveGroup("group3", f"conference.{self.agent.jid.domain}") - ) - await self.wait_for( - LeaveGroup("group4", f"conference.{self.agent.jid.domain}") - ) - await self.agent.stop() - - async def setup(self) -> None: - self.add_behaviour(self.JoinGroups()) diff --git a/examples/agent_groups/group_tags/group_searcher.py b/examples/agent_groups/group_tags/group_searcher.py deleted file mode 100644 index d10f703..0000000 --- a/examples/agent_groups/group_tags/group_searcher.py +++ /dev/null @@ -1,25 +0,0 @@ -# Standard library imports -import logging -from asyncio import sleep - -# Reader imports -from peak import Agent, OneShotBehaviour, SearchGroup - - -class group_searcher(Agent): - class SearchGroups(OneShotBehaviour): - async def run(self) -> None: - await sleep(3) - - def print_groups(tags, groups): - logging.getLogger(self.__class__.__name__).info( - f"List of groups: {str(groups)}" - ) - - await self.wait_for(SearchGroup(["test"], print_groups)) - await self.wait_for(SearchGroup(["awesome"], print_groups)) - await self.wait_for(SearchGroup(["awesome", "cool"], print_groups)) - await self.agent.stop() - - async def setup(self) -> None: - self.add_behaviour(self.SearchGroups()) diff --git a/examples/agent_groups/hello_world_group/agent.py b/examples/agent_groups/hello_world_group/agent.py deleted file mode 100644 index fdcb039..0000000 --- a/examples/agent_groups/hello_world_group/agent.py +++ /dev/null @@ -1,25 +0,0 @@ -# Standard library imports -from asyncio import sleep - -# Reader imports -from peak import Agent, JoinGroup, Message, OneShotBehaviour - - -class agent(Agent): - class HelloWorld(OneShotBehaviour): - async def on_start(self) -> None: - behav = JoinGroup("group1", f"conference.{self.agent.jid.domain}") - self.agent.add_behaviour(behav) - await behav.join() - - async def run(self) -> None: - msg = Message(to=f"group1@conference.{self.agent.jid.domain}") - msg.body = "Hello World" - await self.send_to_group(msg) - await sleep(5) - msg.body = "Goodbye World" - await self.send_to_group(msg) - await self.agent.stop() - - async def setup(self) -> None: - self.add_behaviour(self.HelloWorld()) diff --git a/examples/agent_groups/hello_world_group/agents.yaml b/examples/agent_groups/hello_world_group/agents.yaml deleted file mode 100644 index 48bc242..0000000 --- a/examples/agent_groups/hello_world_group/agents.yaml +++ /dev/null @@ -1,13 +0,0 @@ -defaults: - domain: localhost - log_level: debug - -agents: - agent1: - resource: test - file: agent.py - clones: 2 - - agent2: - file: agent.py - log_level: info \ No newline at end of file diff --git a/examples/chalenge_2/mas.yaml b/examples/chalenge_2/mas.yaml new file mode 100644 index 0000000..ea7996d --- /dev/null +++ b/examples/chalenge_2/mas.yaml @@ -0,0 +1,7 @@ +defaults: + domain: localhost +agents: + tagger: + file: tagger.py + searcher: + file: searcher.py \ No newline at end of file diff --git a/examples/chalenge_2/searcher.py b/examples/chalenge_2/searcher.py new file mode 100644 index 0000000..130fe77 --- /dev/null +++ b/examples/chalenge_2/searcher.py @@ -0,0 +1,19 @@ +from asyncio import sleep + +from peak import Agent, OneShotBehaviour, SearchCommunity + + +class searcher(Agent): + class SearchForTags(OneShotBehaviour): + async def run(self) -> None: + await sleep(3) + + def print_groups(tags, groups): + print(f"List of groups with tags {tags}: {str(groups)}") + + await self.wait_for(SearchCommunity(["test"], print_groups)) + await self.wait_for(SearchCommunity(["awesome"], print_groups)) + await self.agent.stop() + + async def setup(self) -> None: + self.add_behaviour(self.SearchForTags()) diff --git a/examples/chalenge_2/tagger.py b/examples/chalenge_2/tagger.py new file mode 100644 index 0000000..190fe83 --- /dev/null +++ b/examples/chalenge_2/tagger.py @@ -0,0 +1,29 @@ +from asyncio import sleep + +from peak import Agent, JoinCommunity, LeaveCommunity, OneShotBehaviour + + +class tagger(Agent): + class TagCommunities(OneShotBehaviour): + async def run(self) -> None: + self.agent.add_behaviour( + JoinCommunity( + "group1", + f"conference.{self.agent.jid.domain}", + ["test", "awesome"], + ) + ) + self.agent.add_behaviour( + JoinCommunity("group2", f"conference.{self.agent.jid.domain}", ["test"]) + ) + await sleep(10) + await self.wait_for( + LeaveCommunity("group1", f"conference.{self.agent.jid.domain}") + ) + await self.wait_for( + LeaveCommunity("group2", f"conference.{self.agent.jid.domain}") + ) + await self.agent.stop() + + async def setup(self) -> None: + self.add_behaviour(self.TagCommunities()) diff --git a/examples/data_extraction/agent.py b/examples/data_extraction/agent.py index 4fdc81e..c0f2079 100644 --- a/examples/data_extraction/agent.py +++ b/examples/data_extraction/agent.py @@ -1,8 +1,7 @@ -# Standard library imports +import json from random import random -# Reader imports -from peak import Agent, ExportData, PeriodicBehaviour +from peak import Agent, CreateGraph, PeriodicBehaviour class agent(Agent): @@ -35,30 +34,24 @@ async def on_start(self) -> None: 0.198094934, 0.373578536, ] + with open("graph_options.json") as file: + self.graph = json.load(file) async def run(self) -> None: if self.count >= 10: await self.agent.stop() - self.agent.consumption = [ + consumption = [ self.count, self.consumption_data[self.count] * 1000 + random() * 100, ] - self.agent.generation = [ + generation = [ self.count, self.generation_data[self.count] * 1000 + random() * 100, ] + self.graph["series"][0]["data"].append(consumption) + self.graph["series"][1]["data"].append(generation) + self.agent.add_behaviour(CreateGraph("house", self.graph)) self.count += 1 async def setup(self) -> None: - self.consumption = 0 - self.generation = 0 - self.add_behaviour(self.RandomTrial(1)) - self.add_behaviour( - ExportData( - "output.json", - ["consumption", "generation"], - interval=1, - to_graph=True, - graph_name=self.name, - ) - ) + self.add_behaviour(self.RandomTrial(5)) diff --git a/examples/data_extraction/graph_options.json b/examples/data_extraction/graph_options.json index 5fdb14d..ccd4b05 100644 --- a/examples/data_extraction/graph_options.json +++ b/examples/data_extraction/graph_options.json @@ -6,7 +6,7 @@ "trigger": "axis" }, "legend": { - "data": ["Consumer1", "Prosumer1", "Consumer2"] + "data": ["Consumption", "Generation"] }, "grid": { "left": "3%", @@ -20,28 +20,21 @@ } }, "xAxis": { - "type": "category", - "boundaryGap": false, - "data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] + "boundaryGap": false }, "yAxis": { "type": "value" }, "series": [ { - "name": "Consumer1", + "name": "Consumption", "type": "line", - "data": [120, 132, 101, 134, 90, 230, 210] + "data": [] }, { - "name": "Prosumer1", + "name": "Generation", "type": "line", - "data": [220, 182, 191, 234, 290, 330, 310] - }, - { - "name": "Consumer2", - "type": "line", - "data": [150, 232, 201, 154, 190, 330, 410] + "data": [] } ] } \ No newline at end of file diff --git a/examples/data_providers/dataset/agent.py b/examples/data_providers/dataset/agent.py index 3313f49..b4f93db 100644 --- a/examples/data_providers/dataset/agent.py +++ b/examples/data_providers/dataset/agent.py @@ -1,5 +1,4 @@ -# Reader imports -from peak import Agent, JoinGroup, Message, PeriodicBehaviour +from peak import Agent, PeriodicBehaviour class agent(Agent): diff --git a/pyproject.toml b/pyproject.toml index 4b8a231..f08b16e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "peak-mas" -version = "1.0.3" +version = "1.0.5" description = "Python-based framework for heterogeneous agent communities" readme = "README.md" authors = [{ name = "Bruno Ribeiro", email = "brgri@isep.ipp.pt" }] @@ -25,7 +25,6 @@ dependencies = [ "aiohttp_cors", "aioxmpp", "pandas", - "pyModbusTCP", "spade >= 3.2.2", "PyYAML" ] @@ -33,7 +32,7 @@ requires-python = "==3.9.6" [project.optional-dependencies] build = ["build", "twine"] -dev = ["black", "isort", "mypy", "bumpver", "pipreqs"] +dev = ["black", "isort", "mypy", "bumpver", "pipreqs", "autoflake"] [project.urls] Homepage = "https://www.gecad.isep.ipp.pt/peak" @@ -43,7 +42,7 @@ Github = "https://github.com/gecad-group/peak-mas" peak = "peak.__main__:main" [tool.bumpver] -current_version = "1.0.3" +current_version = "1.0.5" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "chore: bump version {old_version} -> {new_version}" commit = true @@ -55,7 +54,4 @@ push = false "src/peak/__init__.py" = ["{version}"] [tool.isort] -profile = "black" -import_heading_stdlib = "Standard library imports" -import_heading_thirdparty = "Third party imports" -import_heading_firstparty = "Reader imports" \ No newline at end of file +profile = "black" \ No newline at end of file diff --git a/src/peak/__init__.py b/src/peak/__init__.py index 54a2d2c..03ff4ee 100644 --- a/src/peak/__init__.py +++ b/src/peak/__init__.py @@ -1,4 +1,4 @@ -"""PEAK - Python-based framework for hEterogeneous Agent Communities. +"""PEAK - Python-based framework for heterogeneous agent communities. PEAK is a framework for developing communities of heterogeneous agents. This communities are multi-agent systems that can coexist and exchange @@ -7,7 +7,7 @@ isort: skip_file """ -import logging as _logging +import logging from spade.message import Message from spade.template import Template @@ -15,18 +15,9 @@ from peak.core import * from peak.agents import * from peak.behaviours import * -from peak.properties import * + +logging.getLogger(__name__).addHandler(logging.NullHandler()) __author__ = "Bruno Ribeiro" __email__ = "brgri@isep.ipp.pt" -__version__ = "1.0.3" - -# filtering noisy loggers -_logging.getLogger("aiosasl").setLevel(_logging.ERROR) -_logging.getLogger("spade").setLevel(_logging.ERROR) -_logging.getLogger("muc").setLevel(_logging.ERROR) -_logging.getLogger("StanzaStream").setLevel(_logging.ERROR) -_logging.getLogger("XMLStream").setLevel(_logging.ERROR) -_logging.getLogger("aioopenssl").setLevel(_logging.ERROR) -_logging.getLogger("aioxmpp").setLevel(_logging.ERROR) -_logging.getLogger("asyncio").setLevel(_logging.ERROR) +__version__ = "1.0.5" diff --git a/src/peak/__main__.py b/src/peak/__main__.py index 2eac2d5..b484fdd 100644 --- a/src/peak/__main__.py +++ b/src/peak/__main__.py @@ -1,99 +1,108 @@ -# Standard library imports import logging import sys from argparse import ArgumentParser from pathlib import Path -# Reader imports from peak import JID from peak import __name__ as peak_name from peak import __version__ as version from peak.cli import df, mas -logger = logging.getLogger() +_logger = logging.getLogger(peak_name) def main(args=None): + _logger.setLevel(logging.INFO) + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter( + logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") + ) + _logger.addHandler(handler) + try: + _main() + except Exception as e: + _logger.critical(e, exc_info=1) + except KeyboardInterrupt: + _logger.info("Stoping PEAK: (reason: KeyboardInterrupt)") + + +def _main(args=None): parser = ArgumentParser(prog=peak_name) parser.add_argument("--version", action="version", version=version) - parser.add_argument( - "-v", - action="store_true", - help="Verbose. Turns on the debug info.", - ) subparsers = parser.add_subparsers(required=True) # parser for "df" command df_parser = subparsers.add_parser( - name="df", help="Execute Directory Facilitator agent." + name="df", help="execute Directory Facilitator agent" ) df_parser.add_argument( "-domain", type=str, default="localhost", - help="XMPP domain to which the DF must register and login. Default is localhost.", + help="XMPP server domain (default: localhost)", ) df_parser.add_argument( - "--verify_security", action="store_true", help="Verifies the SLL certificates." + "--verify_security", action="store_true", help="verify SLL certificates" ) df_parser.add_argument( "-log_level", - type=lambda x: logging.getLevelName(str.upper(x)), - default=logging.getLevelName("INFO"), - help="Selects the logging level of the DF. Default is INFO.", + type=str.upper, + default="INFO", + help="PEAK logging level (default: INFO)", ) df_parser.add_argument( "-port", type=str, default="10000", - help="Port to be opened for the REST API. Default is 10000", + help="REST API port (default: 10000)", ) df_parser.set_defaults(func=df.exec) # parser for the "run" command run_parser = subparsers.add_parser( name="run", - help="Execute PEAK's agents.", + help="execute PEAK's agents", ) run_parser.add_argument( "file", type=Path, - help="This file must be a python file. The Python file must contain a class of a single agent to be executed. The name of the class must be the same of the name of the file. ", + help="Python file containing the class of the agent to be executed (the same name must be used in the class and in the file) ", ) run_parser.add_argument( - "-jid", type=JID.fromstr, help="JID of the agent to be executed.", required=True - ) - run_parser.add_argument( - "-properties", - type=Path, - help="Python file where the agent properties are located.", + "-jid", type=JID.fromstr, help="agent XMPP ID", required=True ) run_parser.add_argument( "-clones", type=int, default=1, - help="The number of clones of the agent. The name of each agent will be the same as the JID but with the number of the corresponding clone to it (e.g. john_0@localhost, john_1@localhost). The sequence starts in zero.", + help="number of clones", ) run_parser.add_argument( "-log_level", - type=lambda x: logging.getLevelName(str.upper(x)), - default=logging.getLevelName("INFO"), - help="Selects the logging level of the agent.", + type=str.upper, + default="INFO", + help="PEAK logging level (default: INFO)", ) run_parser.add_argument( - "--verify_security", action="store_true", help="Verifies the SLL certificates." + "--verify_security", action="store_true", help="verify SLL certificates" ) run_parser.set_defaults(func=mas.agent_exec) - # parser for the "run" command + # parser for the "start" command start_parser = subparsers.add_parser( name="start", - help="Executes multiple agents using a YAML configuration file.", + help="execute a multiagent system using an YAML configuration file", ) start_parser.add_argument( "file", type=Path, - help="Path to the YAML configuration file.", + help="YAML configuration file", + ) + start_parser.add_argument( + "-log_level", + type=str.upper, + default="INFO", + help="PEAK logging level (default: INFO)", ) start_parser.set_defaults(func=mas.multi_agent_exec) @@ -102,15 +111,7 @@ def main(args=None): sys.exit(1) args = parser.parse_args(args) - if args.v: - logger.setLevel("DEBUG") + _logger.setLevel(args.log_level) + _logger.info("Starting PEAK") args.func(**vars(args)) - - -if __name__ == "__main__": - try: - main() - except Exception as e: - logger.exception(e) - except KeyboardInterrupt: - pass + _logger.info("Stoping PEAK") diff --git a/src/peak/agents.py b/src/peak/agents.py index 3fbe80f..57c81d9 100644 --- a/src/peak/agents.py +++ b/src/peak/agents.py @@ -1,79 +1,57 @@ -# Standard library imports import asyncio as _asyncio +import json import logging as _logging from abc import ABCMeta as _ABCMeta from abc import abstractmethod from datetime import datetime, timedelta -from json import dumps as json_dumps -from json import loads as json_loads -# Third party imports import aiohttp_cors from aioxmpp import JID -# Reader imports from peak import Agent, CyclicBehaviour, Message, PeriodicBehaviour, Template -from peak.properties import Properties _logger = _logging.getLogger(__name__) class SyncAgent(Agent, metaclass=_ABCMeta): - """Agent that is synchronized by the Synchronizer. + """Is synchronized by the Synchronizer. Every agent that needs to be synchronized needs to extend this class. - - Attributes: - period: An integer representing the number of the period inside the simulation. - time: A datetime of the current moment in the simmulation. """ class _StepBehaviour(CyclicBehaviour): - """Listens for the Synchronizer's clock's ticks.""" - async def on_start(self): - _logger.info("Waiting for simulation to start...") + self.logger.info("Waiting for simulation to start...") async def run(self): msg = await self.receive(10) if msg: if msg.get_metadata("sync") == "step": - self.agent.period = int(msg.get_metadata("period")) + period = int(msg.get_metadata("period")) + time = None if msg.get_metadata("time"): - self.agent.time = datetime.strptime( + time = datetime.strptime( msg.get_metadata("time"), "%Y-%m-%d %H:%M:%S" ) - if self.agent.period != 0: - self.agent.iterate_properties() - await self.agent.step() - _logger.info( - "Period " - + str(self.agent.period) - + " (" - + str(self.agent.time) - + ")" - ) + await self.agent.step(period, time) + self.logger.info(msg.body) if msg.get_metadata("sync") == "stop": - _logger.info("Simulation ended") + self.logger.info("Simulation ended.") self.kill() async def on_end(self): await self.agent.stop() - def __init__( - self, jid: JID, properties: Properties = None, verify_security: bool = False - ): - """Agent that listens to the Synchronizer. + def __init__(self, jid: JID, verify_security: bool = False): + """Inits the SyncAgent with the JID provided. Args: - jid: XMPP identifier of the agent. - properties: Atributes of the agent to be injected. - verify_security: If True verifies SSL certificates. + jid (:obj:`JID`): Agent's XMPP identifier. + verify_security (bool, optional): If True, verifies the SSL certificates. + Defaults to False. """ - super().__init__(jid, properties, verify_security) - self.period = 0 - self.time = 0 + super().__init__(jid, verify_security) template_step = Template() template_step.set_metadata("sync", "step") template_stop = Template() @@ -82,15 +60,20 @@ def __init__( self.add_behaviour(self._StepBehaviour(), template) @abstractmethod - async def step(self): + async def step(self, period: int, time: datetime = None): """Executed at each tick of the Synchronizer clock. - To be implemented by the user.""" + To be implemented by the user. + + Args: + period (int): Number of the current period. + time (datetime, optional): Current datetime inside the simulation. It must be + configured in the Synchronizer.""" raise NotImplementedError() class Synchronizer(Agent): - """Agent that synchronizes the other agents. + """Synchronizes the syncagents. The Synchronizer creates a group of agents, awaits for the agents to join the group and starts the clock of the @@ -118,10 +101,13 @@ def __init__( self.periods = periods async def on_start(self): - while not len(await self.agent.group_members(self.group_jid)) >= self.n_agents: + self.logger.info("Waiting for all agents to enter the group...") + while ( + not len(await self.agent.group_members(self.group_jid)) >= self.n_agents + ): await _asyncio.sleep(1) self.current_period = 0 - _logger.info("Starting simulation...") + self.logger.info("Starting simulation...") async def run(self): msg = Message() @@ -130,15 +116,15 @@ async def run(self): msg.set_metadata("sync", "stop") self.kill() else: - _logger.info("Period " + str(self.current_period)) - msg.body = "Period " + str(self.current_period) + self.logger.info(f"Period {self.current_period}") + msg.body = f"Period {self.current_period}" msg.set_metadata("sync", "step") msg.set_metadata("period", str(self.current_period)) await self.send_to_group(msg) self.current_period += 1 async def on_end(self): - _logger.info("Ending simulation...") + self.logger.info("Ending simulation...") await self.agent.stop() class _DateTimeClock(PeriodicBehaviour): @@ -166,13 +152,13 @@ def __init__( self.period_time = period_time_simulated async def on_start(self): - _logger.info("Waiting for all agents to enter the group...") + self.logger.info("Waiting for all agents to enter the group...") while ( not len(await self.agent.group_members(self.group_jid)) >= self.n_agents ): await _asyncio.sleep(1) self.current_period = 0 - _logger.info("Starting simulation...") + self.logger.info("Starting simulation...") async def run(self): msg = Message() @@ -181,12 +167,8 @@ async def run(self): msg.set_metadata("sync", "stop") self.kill() else: - _logger.info( - "Period " + str(self.current_period) + " (" + str(self.time) + ")" - ) - msg.body = ( - "Period " + str(self.current_period) + " (" + str(self.time) + ")" - ) + self.logger.info(f"Period {self.current_period} ({self.time})") + msg.body = f"Period {self.current_period} ({self.time})" msg.set_metadata("sync", "step") msg.set_metadata("period", str(self.current_period)) msg.set_metadata( @@ -197,11 +179,11 @@ async def run(self): self.time += self.period_time async def on_end(self): - _logger.info("Ending simulation...") + self.logger.info("Ending simulation...") await self.agent.stop() async def sync_group_period( - self, group_jid: str, n_agents: int, time_per_period: float, periods: int + self, group_jid: str, n_agents: int, interval: float, periods: int ): """Synchronizes a group of agents. @@ -212,39 +194,42 @@ async def sync_group_period( n_agents: Number of agents to be synchronized. Synchronizer awaits for this number of agents to join the group before it starts the simulation. - time_per_period: time in seconds between each period. - periods: number of periods to simulate. + interval: Time in seconds between each period. + periods: Number of periods to simulate. """ await self.join_group(group_jid) - self.add_behaviour( - self._PeriodicClock(group_jid, n_agents, periods, time_per_period) - ) + self.add_behaviour(self._PeriodicClock(group_jid, n_agents, periods, interval)) async def sync_group_time( self, group_jid: str, n_agents: int, - initial_time: datetime, - end_time: datetime, - internal_period_time: timedelta, + initial_datetime: datetime, + end_datetime: datetime, + internal_interval: timedelta, external_period_time: float, start_at: datetime = None, ): """Synchronizes a group of agents. - The clock is based on the date and time inside of the simulation. + Here two time dimensions are created. One is the real-time at which + the clock of the Synchronizer will run. The other is the fictional + datetime created inside the simulation. For example, one second + can correspond to one day inside the simulation. Args: group_jid: Identifier of the XMPP group to be synchronized. n_agents: Number of agents to be synchronized. Synchronizer awaits for this number of agents to join the group before it starts the simulation. - initial_time: defines the internal date and time at the start of the + initial_datetime: Defines the initial date and time inside the simulation. - end_time: defines the internal date and time at which the simulation ends. - internal_period_time: time between each period inside the simulation. - period_time_real: time between each tick of the clock. - start_at: schedules the simulation start at a given time. If None the + end_datetime: Defines the date and time at which the simulation ends. + internal_interval: Time between each period relative to the initial and + end datetimes. + interval: Time in seconds between each period relative to the Synchronizers + clock. + start_at: Schedules the simulation to start at a given time. If None the simulation starts right away. """ await self.join_group(group_jid) @@ -252,9 +237,9 @@ async def sync_group_time( self._DateTimeClock( group_jid, n_agents, - initial_time, - end_time, - internal_period_time, + initial_datetime, + end_datetime, + internal_interval, external_period_time, start_at, ) @@ -269,22 +254,15 @@ class DF(Agent): agents. """ - class _GroupHierarchy(CyclicBehaviour): + class _EcosystemHierarchy(CyclicBehaviour): """Manages the group structure of all the multi-agent systems.""" async def on_start(self): - self.logger = _logging.getLogger(self.__class__.__name__) + self.logger = self.agent.logger.getChild(self.__class__.__name__) self.logger.debug("starting behaviour") template = Template() template.set_metadata("resource", "treehierarchy") self.set_template(template) - self.agent.grouphierarchy_data = { - "nodes": set(), - "links": set(), - "categories": set(), - "node_members": {}, - "tags": {}, - } async def run(self): msg = await self.receive(60) @@ -293,7 +271,7 @@ async def run(self): path = msg.get_metadata("path") domain = msg.get_metadata("domain") if meta_tags := msg.get_metadata("tags"): - tags = json_loads(meta_tags) + tags = json.loads(meta_tags) nodes = path.split("/") self.logger.debug("nodes: " + str(nodes)) level = "level" @@ -301,73 +279,80 @@ async def run(self): if ( msg.get_metadata("leave") and msg.sender - in self.agent.grouphierarchy_data["node_members"][nodes[-1]] + in self.agent.ecosystemhierarchy_data["node_members"][nodes[-1]] ): self.logger.debug(str(msg.sender) + " leaving " + path) - self.agent.grouphierarchy_data["node_members"][nodes[-1]].remove( - msg.sender - ) + self.agent.ecosystemhierarchy_data["node_members"][ + nodes[-1] + ].remove(msg.sender) nodes = nodes[::-1] # remove empty nodes and links for i, node in enumerate(nodes): if len( - self.agent.grouphierarchy_data["node_members"][node] + self.agent.ecosystemhierarchy_data["node_members"][node] ) == 0 and not any( node == source - for source, _ in self.agent.grouphierarchy_data["links"] + for source, _ in self.agent.ecosystemhierarchy_data["links"] ): - self.agent.grouphierarchy_data["node_members"].pop( + self.agent.ecosystemhierarchy_data["node_members"].pop( node ) # this line can be removed, there is no need in removing the node from the dict - self.agent.grouphierarchy_data["nodes"].remove( + self.agent.ecosystemhierarchy_data["nodes"].remove( (node, level + str(len(nodes) - 1 - i), domain) ) if i + 1 < len(nodes): - self.agent.grouphierarchy_data["links"].remove( + self.agent.ecosystemhierarchy_data["links"].remove( (nodes[i + 1], node) ) existing_categories = set() # remove categories if empty - for _, level, _ in self.agent.grouphierarchy_data["nodes"]: + for _, level, _ in self.agent.ecosystemhierarchy_data["nodes"]: existing_categories.add(level) - difference = self.agent.grouphierarchy_data[ + difference = self.agent.ecosystemhierarchy_data[ "categories" ].difference(existing_categories) if any(difference): - self.agent.grouphierarchy_data["categories"] -= difference + self.agent.ecosystemhierarchy_data["categories"] -= difference else: self.logger.debug(str(msg.sender) + " entering " + path) last = None for i, node in enumerate(nodes): - self.agent.grouphierarchy_data["nodes"].add( + self.agent.ecosystemhierarchy_data["nodes"].add( (node, level + str(i), domain) ) - if node not in self.agent.grouphierarchy_data["node_members"]: - self.agent.grouphierarchy_data["node_members"][node] = [] - self.agent.grouphierarchy_data["categories"].add(level + str(i)) + if ( + node + not in self.agent.ecosystemhierarchy_data["node_members"] + ): + self.agent.ecosystemhierarchy_data["node_members"][ + node + ] = [] + self.agent.ecosystemhierarchy_data["categories"].add( + level + str(i) + ) if last != None: - self.agent.grouphierarchy_data["links"].add( + self.agent.ecosystemhierarchy_data["links"].add( ( last, node, ) ) last = node - self.agent.grouphierarchy_data["node_members"][last].append( + self.agent.ecosystemhierarchy_data["node_members"][last].append( msg.sender ) for tag in tags: - if tag not in self.agent.grouphierarchy_data["tags"]: - self.agent.grouphierarchy_data["tags"][tag] = set() - self.agent.grouphierarchy_data["tags"][tag].add(last) + if tag not in self.agent.ecosystemhierarchy_data["tags"]: + self.agent.ecosystemhierarchy_data["tags"][tag] = set() + self.agent.ecosystemhierarchy_data["tags"][tag].add(last) - class _SearchGroup(CyclicBehaviour): + class _SearchCommunity(CyclicBehaviour): """Handles all the requests to search for groups.""" async def on_start(self) -> None: - self.logger = _logging.getLogger(self.__class__.__name__) + self.logger = self.agent.logger.getChild(self.__class__.__name__) self.logger.debug("starting behaviour") template = Template() template.set_metadata("resource", "searchgroup") @@ -376,21 +361,21 @@ async def on_start(self) -> None: async def run(self) -> None: msg = await self.receive(60) if msg and (meta_tags := msg.get_metadata("tags")): - tags = json_loads(meta_tags) - groups: set = self.agent.grouphierarchy_data["tags"][tags[0]] + tags = json.loads(meta_tags) + communities: set = self.agent.ecosystemhierarchy_data["tags"][tags[0]] for tag in tags[1:]: - groups = groups.intersection( - self.agent.grouphierarchy_data["tags"][tag] + communities = communities.intersection( + self.agent.ecosystemhierarchy_data["tags"][tag] ) res = msg.make_reply() - res.set_metadata("groups", json_dumps(list(groups))) + res.set_metadata("communities", json.dumps(list(communities))) await self.send(res) class _CreateGraph(CyclicBehaviour): """Handles the requests to create graphs""" async def on_start(self): - self.logger = _logging.getLogger(self.__class__.__name__) + self.logger = self.agent.logger.getChild(self.__class__.__name__) self.logger.debug("starting behaviour") template = Template() template.set_metadata("resource", "graph") @@ -400,47 +385,17 @@ async def on_start(self): async def run(self) -> None: msg = await self.receive(60) if msg: - self.logger.debug(msg.body) - graph_name = msg.get_metadata("graph_name") - graph_options = msg.get_metadata("graph_options") - properties = json_loads(msg.get_metadata("properties")) - self.agent.dataanalysis_data[graph_name] = { - "graph_options": graph_options, - "data": {}, - } - for property in properties: - self.agent.dataanalysis_data[graph_name]["data"][property] = [] - - class _UpdateGraph(CyclicBehaviour): - """Handles the requests to update the data of a given graph.""" - - async def on_start(self) -> None: - self.logger = _logging.getLogger(self.__class__.__name__) - self.logger.debug("starting behaviour") - template = Template() - template.set_metadata("resource", "graph") - template.set_metadata("action", "update") - self.set_template(template) - - async def run(self) -> None: - msg = await self.receive(60) - if msg: - self.logger.debug(msg.body) - graph_name = msg.get_metadata("graph_name") - data = json_loads(msg.get_metadata("data")) - for property in data: - self.agent.dataanalysis_data[graph_name]["data"][property].append( - data[property] - ) - self.logger.debug( - 'updating property "' + property + '" : ' + str(data[property]) - ) + self.logger.debug(msg) + id = msg.get_metadata("id") + graph = json.loads(msg.get_metadata("graph")) + self.agent.dataanalysis_data[id] = graph def __init__(self, domain, verify_security, port): super().__init__( JID.fromstr("df@" + domain + "/admin"), verify_security=verify_security ) self.port = port + self.logger = _logger.getChild(self.__class__.__name__) @classmethod def name(cls, domain: str) -> str: @@ -455,14 +410,19 @@ def name(cls, domain: str) -> str: return "df@" + domain + "/admin" async def setup(self): - self.grouphierarchy_data = dict() + self.ecosystemhierarchy_data = { + "nodes": set(), + "links": set(), + "categories": set(), + "node_members": {}, + "tags": {}, + } self.dataanalysis_data = dict() self.group_tags = dict() - self.add_behaviour(self._GroupHierarchy()) - self.add_behaviour(self._SearchGroup()) + self.add_behaviour(self._EcosystemHierarchy()) + self.add_behaviour(self._SearchCommunity()) self.add_behaviour(self._CreateGraph()) - self.add_behaviour(self._UpdateGraph()) # Create routes. self.web.add_get("/groups", self.get_groups, template=None) @@ -487,14 +447,14 @@ async def setup(self): # Start web API self.web.start(port=self.port) - _logger.info("REST API running on port " + self.port) + self.logger.info("REST API running on port " + self.port) async def get_groups(self, request): return { - "nodes": list(self.grouphierarchy_data["nodes"]), - "links": list(self.grouphierarchy_data["links"]), - "categories": list(self.grouphierarchy_data["categories"]), - "node_members": self.grouphierarchy_data["node_members"], + "nodes": list(self.ecosystemhierarchy_data["nodes"]), + "links": list(self.ecosystemhierarchy_data["links"]), + "categories": list(self.ecosystemhierarchy_data["categories"]), + "node_members": self.ecosystemhierarchy_data["node_members"], } async def refresh_groups(self, request): diff --git a/src/peak/behaviours.py b/src/peak/behaviours.py index 3c54f22..61daef3 100644 --- a/src/peak/behaviours.py +++ b/src/peak/behaviours.py @@ -1,17 +1,13 @@ -# Standard library imports +import json import logging -from json import dumps as json_dumps -from json import loads as json_loads from typing import Callable -# Reader imports -from peak import DF, Message, SyncAgent, Template -from peak.core import CyclicBehaviour, OneShotBehaviour, PeriodicBehaviour -from peak.properties import Property +from peak import DF, Message, Template +from peak.core import OneShotBehaviour -class JoinGroup(OneShotBehaviour): - """Joins a group using a JID.""" +class JoinCommunity(OneShotBehaviour): + """Joins a community using a JID.""" def __init__(self, path: str, domain: str, tags: list = []): super().__init__() @@ -25,17 +21,17 @@ async def run(self): msg.set_metadata("resource", "treehierarchy") msg.set_metadata("path", self.path) msg.set_metadata("domain", self.domain) - msg.set_metadata("tags", json_dumps(self.tags)) + msg.set_metadata("tags", json.dumps(self.tags)) await self.send(msg) nodes = self.path.split("/") for node in nodes[:-1]: - await self.agent.join_group(node + "_down@" + self.domain) - await self.agent.join_group(nodes[-1] + "@" + self.domain) - await self.agent.join_group(nodes[-1] + "_down@" + self.domain) + await self.join_community(node + "_down@" + self.domain) + await self.join_community(nodes[-1] + "_down@" + self.domain) + await self.join_community(nodes[-1] + "@" + self.domain) -class LeaveGroup(OneShotBehaviour): - """Leaves a group.""" +class LeaveCommunity(OneShotBehaviour): + """Leaves a community.""" def __init__(self, path: str, domain: str): super().__init__() @@ -52,13 +48,13 @@ async def run(self): await self.send(msg) nodes = self.path.split("/") for node in nodes[:-1]: - await self.agent.leave_group(node + "_down@" + self.domain) - await self.agent.leave_group(nodes[-1] + "@" + self.domain) - await self.agent.leave_group(nodes[-1] + "_down@" + self.domain) + await self.leave_community(node + "_down@" + self.domain) + await self.leave_community(nodes[-1] + "@" + self.domain) + await self.leave_community(nodes[-1] + "_down@" + self.domain) -class SearchGroup(OneShotBehaviour): - """Searches for a group.""" +class SearchCommunity(OneShotBehaviour): + """Searches for a community.""" def __init__( self, tags: list[str], callback: Callable[[list[str]], None], *args, **kargs @@ -69,218 +65,46 @@ def __init__( self.args = args self.kargs = kargs + async def on_start(self): + template = Template() + template.set_metadata("resource", "searchgroup") + self.set_template(template) + async def run(self): msg = Message() msg.to = DF.name(self.agent.jid.domain) msg.set_metadata("resource", "searchgroup") - msg.set_metadata("tags", json_dumps(self.tags)) + msg.set_metadata("tags", json.dumps(self.tags)) await self.send(msg) - res = None - while not res: - res = await self.receive(60) - if not res: - raise Exception("DF did not respond") - groups = json_loads(res.get_metadata("groups")) - logging.getLogger(self.__class__.__name__).debug( - f"search: {str(self.tags)}, result: {str(groups)}" - ) - self.callback(self.tags, groups, *self.args, **self.kargs) - - -class ExportData(OneShotBehaviour): - """Exports data to a file and optionaly to the DF. - - The data exports works diferently when using the Synchronizer and - when its not used. When the Synchronizer its being used the data - is exported at the same rate as the Synchronizers clock. If its not - used the user must define a interval in which the data is exported. - - Warning: the file will be overwriten.""" - - def __init__( - self, - file_name: str, - properties: list, - interval: int = None, - to_graph: bool = False, - graph_name: str = "", - graph_options: str = "", - ): - super().__init__() - self.interval = interval - self.file_name = file_name - self.properties = properties - self.to_graph = to_graph - if graph_name == "" and self.to_graph: - raise Exception("when graph is set to true, a name for it must be defined") - self.graph_name = graph_name - self.graph_options = graph_options + res = await self.receive(60) + if res is None: + raise Exception("DF did not respond") + communities = json.loads(res.get_metadata("communities")) + logging.getLogger(self.__class__.__name__).debug( + f"search: {str(self.tags)}, result: {str(communities)}" + ) + self.callback(self.tags, communities, *self.args, **self.kargs) - async def run(self) -> None: - logger = logging.getLogger(self.__class__.__name__) - if isinstance(self.agent, SyncAgent): - logger.debug("synchronizer detected") - self.agent.add_behaviour( - _ExportDataSync( - self.file_name, - self.properties, - self.to_graph, - self.graph_name, - self.graph_options, - ) - ) - else: - logger.debug("synchronizer not detected") - self.agent.add_behaviour( - _ExportData( - self.file_name, - self.properties, - self.interval, - self.to_graph, - self.graph_name, - self.graph_options, - ) - ) +class CreateGraph(OneShotBehaviour): + """Creates a graph in the dashboard. -class _ExportDataSync(CyclicBehaviour): - """Exports the data at the same rate as the synchronization.""" + Sends the graph configuration and data to the DF so he can host it. + """ - def __init__( - self, - file_name: str, - properties: list, - to_graph: bool, - graph_name: str, - graph_options: str, - ): + def __init__(self, id: str, graph: dict): super().__init__() - self.file_name = file_name - self.graph_name = graph_name - self.properties = properties - self.to_graph = to_graph - self.graph_options = graph_options - - async def on_start(self) -> None: - self.logger = logging.getLogger(self.__class__.__name__) - self.logger.info("behaviour initiated") - template_step = Template() - template_step.set_metadata("sync", "step") - template_stop = Template() - template_stop.set_metadata("sync", "stop") - template = template_step | template_stop - self.set_template(template) - - self.file_data = {} - for property in self.properties: - self.file_data[property] = [] - if self.to_graph: - msg = Message(to=DF.name(self.agent.jid.domain)) - msg.body = "Create graph " + self.graph_name - msg.metadata = { - "resource": "graph", - "action": "create", - "graph_name": self.graph_name, - "graph_options": self.graph_options, - "properties": json_dumps(self.properties), - } - await self.send(msg) - self.logger.info("creating graph " + self.graph_name) - - async def run(self) -> None: - msg = await self.receive(60) - if msg: - if msg.get_metadata("sync") == "step": - period = msg.get_metadata("period") - current_data = dict() - for property in self.file_data: - attribute = getattr(self.agent, property) - if type(attribute) is Property: - self.file_data[property].append(attribute.current_value) - current_data[property] = [period, attribute.current_value] - else: - self.file_data[property].append(attribute) - current_data[property] = [period, attribute] - if self.to_graph: - msg = Message(to=DF.name(self.agent.jid.domain)) - msg.body = "Update graph " + self.graph_name - msg.metadata = { - "resource": "graph", - "action": "update", - "graph_name": self.graph_name, - "data": json_dumps(current_data), - } - await self.send(msg) - self.logger.info("updating graph " + self.graph_name) - if msg.get_metadata("sync") == "stop": - with open(self.file_name, "w") as f: - f.write(json_dumps(self.data)) - - -class _ExportData(PeriodicBehaviour): - """Exports the data at a rate defined by the user.""" - - def __init__( - self, - file_name: str, - properties: list, - interval: float, - to_graph: bool, - graph_name: str, - graph_options: str, - ): - super().__init__(interval, None) - self.file_name = file_name - self.graph_name = graph_name - self.properties = properties - self.to_graph = to_graph - self.graph_options = graph_options - - async def on_start(self) -> None: - self.logger = logging.getLogger(self.__class__.__name__) - self.logger.info("exporting " + str(self.properties)) - self.file_data = {} - for property in self.properties: - self.file_data[property] = [] - if self.to_graph: - msg = Message(to=DF.name(self.agent.jid.domain)) - msg.body = "Create graph " + self.graph_name - msg.metadata = { - "resource": "graph", - "action": "create", - "graph_name": self.graph_name, - "graph_options": self.graph_options, - "properties": json_dumps(self.properties), - } - await self.send(msg) - self.logger.info("creating graph " + self.graph_name) + self.id = id + self.graph = graph async def run(self) -> None: - current_data = dict() - for property in self.file_data: - attribute = getattr(self.agent, property) - if type(attribute) is Property: - self.file_data[property].append(attribute.current_value) - current_data[property] = attribute.current_value - else: - self.file_data[property].append(attribute) - current_data[property] = attribute - self.logger.debug( - 'exporting property "' + property + '": ' + str(current_data[property]) - ) - if self.to_graph: - msg = Message(to=DF.name(self.agent.jid.domain)) - msg.body = "Update graph " + self.graph_name - msg.metadata = { - "resource": "graph", - "action": "update", - "graph_name": self.graph_name, - "data": json_dumps(current_data), - } - await self.send(msg) - self.logger.debug("updating graph " + self.graph_name) - - async def on_end(self) -> None: - self.logger.info('exporting to file "' + self.file_name + '"') - with open(self.file_name, "w") as f: - f.write(json_dumps(self.file_data)) + msg = Message() + msg.to = DF.name(self.agent.jid.domain) + msg.body = f"Create graph {self.id}" + msg.metadata = { + "resource": "graph", + "action": "create", + "id": self.id, + "graph": json.dumps(self.graph), + } + await self.send(msg) diff --git a/src/peak/bootloader.py b/src/peak/bootloader.py index 64268a2..4b21963 100644 --- a/src/peak/bootloader.py +++ b/src/peak/bootloader.py @@ -1,25 +1,50 @@ -# Standard library imports +import asyncio import importlib import logging import os import sys import time +from multiprocessing import Process from pathlib import Path -from typing import Type +from typing import List, Type -# Third party imports from aioxmpp import JID from spade import quit_spade logger = logging.getLogger(__name__) +async def _wait_for_processes(processes): + def join_process(process): + process.join() + if process.exitcode != 0: + logger.error(f"{process.name}'s process ended unexpectedly.") + + await asyncio.gather( + *[asyncio.to_thread(join_process, process) for process in processes] + ) + + +def bootloader(agents: list): + logger.info("Loading agents") + procs: List[Process] = [] + for i, agent in enumerate(agents): + proc = Process( + target=boot_agent, + kwargs=agent, + daemon=False, + name=agents[i]["jid"].localpart, + ) + proc.start() + procs.append(proc) + logger.info("Agents loaded") + asyncio.run(_wait_for_processes(procs)) + + def boot_agent( file: Path, jid: JID, - name: str, - number: int, - properties: Path, + cid: int, log_level: int, verify_security: bool, ): @@ -29,68 +54,46 @@ def boot_agent( file: File path where the agent's class is. jid: JID of the agent. name: The name of the agent. - number: If the agent its a clone is the number of present in - the name of the agent, else its None. - properties: Properties to be injected in the agent. - log_level: Logging level to be used in the agents logging file. + cid: Clone ID, zero if its the original. verify_security: If true it validates the SSL certificates. """ - log_file_name: str = jid.localpart + ("_" + jid.resource if jid.resource else "") + log_file_name: str = jid.localpart + (f"_{jid.resource}" if jid.resource else "") logs_folder = file.parent.absolute().joinpath("logs") log_file = logs_folder.joinpath(f"{log_file_name}.log") os.makedirs(logs_folder, exist_ok=True) - logging.basicConfig( - filename=log_file, - filemode="w", - level=log_level, - format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", + sys.stdout = open(log_file, "a", buffering=1) + sys.stderr = sys.stdout + handler = logging.FileHandler(log_file) + handler.setFormatter( + logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") ) + logger.parent.handlers = [] + logger.parent.addHandler(handler) + logger.parent.setLevel(log_level) + handler.setLevel(log_level) - _boot_agent(file, jid, name, number, properties, verify_security) - - -def _boot_agent( - file: Path, - jid: JID, - name: str, - number: int, - properties: Path, - verify_security: bool, -): - """Boots the agent.""" - # Gets the agent and properties classes. Creates the agent with the - # properties and the attributes already provided. Runs the agent and - # creates a loop that waits until the agent dies. - logger.debug("creating agent") + logger.info("Creating agent from file") agent_class = _get_class(file) - file_abs_path = file.parent.absolute() - if properties: - os.chdir(properties.parent) - properties = _get_class(properties)(jid.localpart, name, number) - properties = properties.extract() - else: - properties = None - - os.chdir(file_abs_path) - agent_instance = agent_class(jid, properties, verify_security) - logger.info("starting agent") + os.chdir(file.parent.absolute()) + agent_instance = agent_class(jid, cid, verify_security) + try: + logger.info("Agent starting") agent_instance.start().result() - except Exception as err: - logger.exception(f"could not start the agent: {err}") - while agent_instance.is_alive(): - try: + logger.info("Agent initialized") + while agent_instance.is_alive(): time.sleep(1) - except Exception as e: - logger.error("AGENT CRACHED") - logger.exception(e) - agent_instance.stop() - except KeyboardInterrupt: - logger.info("Keyboard Interrupt") - agent_instance.stop() - quit_spade() - logger.info("agent stoped") + except Exception as error: + logger.exception(f"Stoping agent (reason: {error.__class__.__name__})") + agent_instance.stop().result() + raise SystemExit(1) + except KeyboardInterrupt: + logger.info(f"Stoping agent (reason: KeyboardInterrupt)") + agent_instance.stop().result() + finally: + quit_spade() + logger.info("Agent stoped") def _get_class(file: Path) -> Type: @@ -106,8 +109,13 @@ def _get_class(file: Path) -> Type: Returns: A class object with the same name as the file. """ - module_path, module_file = os.path.split(file.absolute()) - module_name = module_file.split(".")[0] - sys.path.append(module_path) - module = importlib.import_module(module_name) - return getattr(module, module_name) + try: + module_path, module_file = os.path.split(file.absolute()) + module_name = module_file.split(".")[0] + sys.path.append(module_path) + module = importlib.import_module(module_name) + return getattr(module, module_name) + except ModuleNotFoundError: + raise ModuleNotFoundError( + f"the file does not exist or the file name wasn't used as the agent's class name ({file})" + ) diff --git a/src/peak/cli/df.py b/src/peak/cli/df.py index 74c7b75..fca6839 100644 --- a/src/peak/cli/df.py +++ b/src/peak/cli/df.py @@ -1,12 +1,10 @@ -# Standard library imports import logging from time import sleep -# Reader imports from peak import DF -def exec(log_level: int, domain: str, verify_security: bool, port: int, *args, **kargs): +def exec(domain: str, verify_security: bool, port: int, *args, **kargs): """Executes the Directory Facilitator agent. Args: @@ -15,12 +13,11 @@ def exec(log_level: int, domain: str, verify_security: bool, port: int, *args, * verify_security: Verifies the SSL certificates. port: Port to be used by the DF REST API. """ - logging.basicConfig( - level=log_level, - format="%(asctime)s [%(levelname)s] %(message)s", - handlers=[logging.StreamHandler()], + logger = logging.getLogger(__name__).parent + logger.handlers[1].setFormatter( + logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") ) - logger = logging.getLogger(__name__) + logger.info("Starting DF") df = DF(domain, verify_security, port) df.start().result() diff --git a/src/peak/cli/mas.py b/src/peak/cli/mas.py index 97800fb..574a5e7 100644 --- a/src/peak/cli/mas.py +++ b/src/peak/cli/mas.py @@ -1,130 +1,101 @@ -# Standard library imports -from argparse import ArgumentError, ArgumentTypeError +from argparse import ArgumentTypeError from logging import getLevelName, getLogger -from multiprocessing import Process from os import chdir from pathlib import Path -# Third party imports import yaml -# Reader imports from peak import JID -from peak.bootloader import boot_agent +from peak.bootloader import bootloader -_logger = getLogger() +_logger = getLogger(__name__) def agent_exec( file: Path, jid: JID, - properties: Path = None, clones: int = 1, log_level: int = getLevelName("INFO"), verify_security: bool = False, *args, **kargs, ): - """Executes and configures a single agent. - - The arguments args and kargs are used to ignore extra parameters given be the parser. + """Executes a single agent. Args: file: Path to the agent's python file. - properties: Path to the agent's properties python file. jid: JID of the agent. clones: Number of clones to be made. - log_leve: Logging level. + log_level: Logging level. verify_security: Verifies the SSL certificates. - - Raises: - ArgumentTypeError if the file provided is not a python file. """ - - log_level = getLevelName(log_level) - for f in [file, properties]: - if f and not f.is_file(): - raise ArgumentTypeError("'{}' must be a python file".format(f)) + # TODO: verify at argsparser level + if file and not file.is_file(): + raise ArgumentTypeError(f"Agent's file must be a python file, not '{file}'.") kwargs = { "file": file, "jid": jid, - "name": jid.localpart, - "number": None, - "properties": properties, + "cid": 0, "log_level": log_level, "verify_security": verify_security, } - if clones == 1: - boot_agent(**kwargs) - else: - procs = [] - for i in range(clones): - kwargs["jid"] = jid.replace(localpart=jid.localpart + str(i)) - kwargs["number"] = i - proc = Process(target=boot_agent, kwargs=kwargs, daemon=False) - proc.start() - procs.append(proc) - for proc in procs: - try: - proc.join() - except Exception as e: - _logger.exception(e) - except KeyboardInterrupt: - break + name = jid.localpart + agents = [] + for cid in range(clones): + agent = kwargs.copy() + agents.append(agent) + kwargs["jid"] = kwargs["jid"].replace(localpart=f"{name}{cid}") + kwargs["cid"] = cid + bootloader(agents) -def multi_agent_exec(file: Path, *args, **kargs): - """Executes multiple agents using a YAML configuration file. - - The arguments args and kargs are used to ignore extra parameters given be the parser. +def multi_agent_exec(file: Path, log_level, *args, **kargs): + """Executes agents using a YAML configuration file. Args: file: Path to the agent's python file. """ + + _logger.info("Parsing YAML file") defaults = { "file": None, "domain": None, "resource": None, "ssl": False, - "log_level": "info", - "properties": None, + "log_level": log_level, "clones": 1, } - _logger.debug("parsing yaml file") + agents = [] + with file.open() as f: yml = yaml.full_load(f) chdir(file.parent) + if "defaults" in yml: defaults = defaults | yml["defaults"] - else: - _logger.debug("no defaults in yaml file") if "agents" not in yml: - raise Exception("agents argument required") - _logger.debug("initialize processes") - procs = [] - for agent, agent_args in yml["agents"].items(): - agent_args = defaults | agent_args - + raise Exception("YAML: 'agents' argument required") + for agent_name, agent_args in yml["agents"].items(): + if agent_args is not None: + agent_args = defaults | agent_args + else: + agent_args = defaults if agent_args["file"] is None: - raise Exception(f"{agent}: file argument required") + raise Exception(f"{agent_name}: file argument required") if agent_args["domain"] is None: - raise Exception(f"{agent}: domain argument required") - agent_args["file"] = Path(agent_args["file"]) - if agent_args["properties"]: - agent_args["properties"] = Path(agent_args["properties"]) - agent_args["jid"] = JID(agent, agent_args["domain"], agent_args["resource"]) - agent_args["log_level"] = agent_args["log_level"].upper() - - proc = Process(target=agent_exec, kwargs=agent_args, daemon=False) - proc.start() - procs.append(proc) - _logger.debug("wait for processes to finish") - for proc in procs: - try: - proc.join() - except Exception as e: - _logger.exception(e) - except KeyboardInterrupt: - break + raise Exception(f"{agent_name}: domain argument required") + kwargs = { + "file": Path(agent_args["file"]), + "jid": JID(agent_name, agent_args["domain"], agent_args["resource"]), + "cid": 0, + "log_level": agent_args["log_level"], + "verify_security": agent_args["ssl"], + } + for cid in range(agent_args["clones"]): + agent = kwargs.copy() + agents.append(agent) + kwargs["jid"] = kwargs["jid"].replace(localpart=f"{agent_name}{cid}") + kwargs["cid"] = cid + bootloader(agents) diff --git a/src/peak/core.py b/src/peak/core.py index 9ee2189..561375a 100644 --- a/src/peak/core.py +++ b/src/peak/core.py @@ -1,32 +1,35 @@ -# Standard library imports -import asyncio as _asyncio import logging as _logging from abc import ABCMeta as _ABCMeta -from typing import Any, List +from typing import Dict, List -# Third party imports import aioxmpp as _aioxmpp import spade as _spade from aioxmpp import JID +from aioxmpp.callbacks import first_signal -_logger = _logging.getLogger(__name__) +_module_logger = _logging.getLogger(__name__) -class _XMPPAgent(_spade.agent.Agent): - """Agent that integrates XMPP functionalities. +class Agent(_spade.agent.Agent): + """PEAK's base agent. Attributes: - jid: XMPP identifier. - verify_security: If true verifies the SSL certificates. + communities (dict of :obj:`Room`): Dictionary of the communities joined. + cid (int): Clone ID. """ - def __init__(self, jid: JID, verify_security: bool = False): - _logging.getLogger(jid.localpart).setLevel(_logging.ERROR) - self.groups = dict() - self.muc_client = None - pw = str(jid.bare()) - jid = str(jid) - super().__init__(jid, pw, verify_security) + def __init__(self, jid: JID, cid: int = 0, verify_security: bool = False): + """Inits an agent with a JID. + + Args: + jid (:obj:`JID`): The agent XMPP identifier. + verify_security (bool, optional): If True, verifies the SSL certificates. + Defaults to False. + """ + super().__init__(str(jid), str(jid.bare()), verify_security) + self.communities: Dict[str, _aioxmpp.muc.Room] = dict() + self.cid = cid + self._muc_client = None async def _hook_plugin_after_connection(self): """Executed after SPADE Agent's connection. @@ -35,10 +38,8 @@ async def _hook_plugin_after_connection(self): adds a message dispatcher for the group(MUC) messages. """ self.presence.approve_all = True - - self.muc_client: _aioxmpp.MUCClient = self.client.summon(_aioxmpp.MUCClient) - self.disco: _aioxmpp.DiscoClient = self.client.summon(_aioxmpp.DiscoClient) - + self._muc_client: _aioxmpp.MUCClient = self.client.summon(_aioxmpp.MUCClient) + self._disco: _aioxmpp.DiscoClient = self.client.summon(_aioxmpp.DiscoClient) self.message_dispatcher.register_callback( _aioxmpp.MessageType.GROUPCHAT, None, @@ -50,135 +51,129 @@ async def _hook_plugin_after_connection(self): self._message_received, ) - def _on_muc_failure_handler(self, exc): - """Handles MUC failed connections.""" - _logger.critical("Failed to enter MUC room") - raise exc +class _Behaviour: + """Adds XMPP functinalities to SPADE's base behaviours. - async def join_group(self, jid: str): - """Joins the agent in a group. + Acts as Mixin in the SPADE's behaviours. + + Attributes: + logger (:obj:`Logger`): Used to log every event in a behaviour.""" + + agent: Agent + _logger = _module_logger.getChild("_Behaviour") + + async def join_community(self, jid: str): + """Joins a community. Args: - jid: Group's XMPP identifier. + jid (str): XMPP identifier of the community. Raises: - Exception if the group JID is invalid. + Exception if the community JID is invalid. """ - room, fut = self.muc_client.join(_aioxmpp.JID.fromstr(jid), self.name) - room.on_failure.connect(self._on_muc_failure_handler) - await _asyncio.wait([fut], timeout=3) - if fut.done(): - if jid not in self.groups: - self.groups[jid] = room - _logger.info("joined group: " + jid) + if jid not in self.agent.communities: + room, _ = self.agent._muc_client.join( + _aioxmpp.JID.fromstr(jid), self.agent.name + ) + try: + await first_signal(room.on_enter, room.on_failure) + self.agent.communities[jid] = room + self._logger.debug(f"Joined community: {jid}") + except Exception as error: + self._logger.exception( + f"Couldn't join community (reason: {error}): {jid}" + ) else: - raise Exception("invalid group JID: " + str(jid)) + self._logger.debug(f"Already joined this community: {jid}") - async def leave_group(self, jid: str): - """Leaves a group. + async def leave_community(self, jid: str): + """Leaves a community. Args: - jid: Group's XMPP identifier. + jid (str): XMPP identifier of the community. """ - room = self.groups.pop(jid, None) + room = self.agent.communities.pop(jid, None) if room: - _logger.info("leaving group: " + jid) await room.leave() + self._logger.debug(f"Left community: {jid}") - async def list_groups(self, node_jid: str): - """Retrieves the list of the existing groups. + async def list_communities(self, node_jid: str): + """Retrieves the list of the existing community in the server. + + This method uses the Service Discovery functionality of the XMPP + server. In orther to work the server must have this functionality + configured. Args: - node_jid: JID of the domain responsable for the MUC functionality. + jid: XMPP identifier of the Service Discovery domain. Returns: - A list of XMPP groups. + A list of XMPP communities. """ - info = await self.disco.query_items( + info = await self.agent._disco.query_items( _aioxmpp.JID.fromstr(node_jid), require_fresh=True ) return info.items - async def group_members(self, jid: str) -> List: - """Extracts list of group members from a group. + async def community_members(self, jid: str) -> List[_aioxmpp.muc.Occupant]: + """Retrieves list of members from a community. + + If the agent is not a member of the community, it will enter the room , + retrieve the list of members and then leave the community. Args: - jid: Group's XMPP identifier. + jid: XMPP identifier of the community. Returns: - A copy of the list of occupants. The local user is always the first item in the list. + The list of :obj:`Occupants`. The agent is always the first item in + the list, unless it's not a member. """ - if jid in self.groups: - return self.groups[jid].members + if jid in self.agent.communities: + return self.agent.communities[jid].members else: - await self.join_group(jid) - members = self.groups[jid].members - await self.leave_group(jid) - return members + await self.join_community(jid) + members = self.agent.communities[jid].members + await self.leave_community(jid) + return members[1:] + async def send_to_community(self, msg: _spade.message.Message): + """Sends a message to a community. -class Agent(_XMPPAgent): - """PEAK's base agent. - - Attributes: - jid: XMPP identifier. - properties: Properties to be injected in the agent. - verify_security: If True, it verifies the SSL certificates. - """ - - def __init__(self, jid: JID, properties: Any = None, verify_security: bool = False): - """Inits Agent and fills it with properties.""" - super().__init__(jid, verify_security=verify_security) - if properties: - self.properties = properties - for key in properties: - setattr(self, key, properties[key]) - - def iterate_properties(self): - """Iterates one index over the properties.""" - if hasattr(self, "properties"): - for key in self.properties: - attr = getattr(self, key) - if attr: - getattr(self, key).next() - - -class _Behaviour: - """Adds functinality to the SPADE base behaviours.""" - - agent: Agent - - async def send_to_group(self, msg: _spade.message.Message): - """Sends a message to a group. - - If the agent is not in the group, the agent enters the room first, - sends the message and then leaves the group. + If the agent is not a member of the community, the agent enters the room first, + sends the message and then leaves the community. Args: msg: The XMPP message. """ raw_msg = msg.prepare() - _logger.debug(f"sending message '{msg.body}' to {msg.to}") + self._logger.debug(f"Sending message: {msg}") + group = str(msg.to) try: - await self.agent.groups[str(msg.to)].send_message(raw_msg) + await self.agent.communities[group].send_message(raw_msg) except: - _logger.debug(f"agent not member of {msg.to}, sending message anyway") - room, future = self.agent.muc_client.join(msg.to, self.agent.name) - await future - await room.send_message(raw_msg) - await room.leave() - _logger.debug(f"leaving {msg.to}") - - async def wait_for(self, behaviour: _spade.behaviour.CyclicBehaviour, template: _spade.template.Template = None): - """Awaits synchronozly for a behaviour. + self._logger.debug( + f"Sending a message to a group which the agent is not a member of: {group}" + ) + await self.join_community(group) + await self.agent.communities[group].send_message(raw_msg) + await self.leave_community(group) + + async def wait_for( + self, + behaviour: _spade.behaviour.CyclicBehaviour, + template: _spade.template.Template = None, + ): + """Awaits synchronously for a behaviour. Executes behaviour first, if not executed. - It is used to chain behviour that are dependent on each other. + It is used to chain behviours that are dependent on each other. Args: behaviour: SPADE's behaviour. + tempalte: SPADE's template. """ + self._logger.debug(f"Waiting for behaviour: {behaviour}") if not behaviour.is_running: self.agent.add_behaviour(behaviour, template) await behaviour.join() @@ -187,18 +182,19 @@ async def wait_for(self, behaviour: _spade.behaviour.CyclicBehaviour, template: class OneShotBehaviour( _spade.behaviour.OneShotBehaviour, _Behaviour, metaclass=_ABCMeta ): - pass + """This behaviour is only executed once.""" class PeriodicBehaviour( _spade.behaviour.PeriodicBehaviour, _Behaviour, metaclass=_ABCMeta ): - pass + """This behaviour is executed periodically with an interval.""" class CyclicBehaviour(_spade.behaviour.CyclicBehaviour, _Behaviour, metaclass=_ABCMeta): - pass + """This behaviour is executed cyclically until it is stopped.""" class FSMBehaviour(_spade.behaviour.FSMBehaviour, _Behaviour, metaclass=_ABCMeta): - pass + """A behaviour composed of states (oneshotbehaviours) that may transition from one + state to another.""" diff --git a/src/peak/drivers.py b/src/peak/drivers.py deleted file mode 100644 index 5fa90ae..0000000 --- a/src/peak/drivers.py +++ /dev/null @@ -1,76 +0,0 @@ -# Standard library imports -import logging -from abc import ABCMeta - -# Third party imports -from pyModbusTCP.client import ModbusClient -from pyModbusTCP.utils import get_2comp - -log = logging.getLogger("peak.mas.drivers") - - -class Driver(metaclass=ABCMeta): - """Base interface for the Drivers.""" - - pass - - -class DriverModBusTCP(Driver): - """Protocol ModBus/TCP. - - Attributes: - client: handles the ModBus/TCP comunication. - """ - - def __init__(self, host: str, unit_id: int = None): - """Inits Driver ModBus/TCP. - - Args: - host: Hostname or IPv4/IPv6 address of the device. - unit_id: Unit ID. - """ - self.client = ModbusClient(host=host, unit_id=unit_id, auto_open=True) - - def read(self, register: int, unit_id: int = None) -> int: - """Reads a given register of the device. - - Args: - register: Register address (0 to 65535). - unit_id: Unit ID. - - Returns: - An integer that was read at the given register. - Returns None if the value could not read from the - target. - """ - if unit_id is not None: - self.client.unit_id(unit_id) - data = self.client.read_input_registers(reg_addr=register) - if not data: - log.warning( - "%s cannot read from target %s.", - self.__class__, - self.client.host(), - exc_info=self.client.last_except_txt(), - ) - return None - else: - return get_2comp(data[0]) - - def write(self, register: int, value: int, unit_id: int = None): - """Writes a value in a given register. - - Args: - register: Register address (0 to 65535). - value: Value to be written. - unit_id: Unit ID. - """ - if unit_id is not None: - self.client.unit_id(unit_id) - if not self.client.write_single_register(register, get_2comp(value)): - log.warning( - "%s cannot write to target %s.", - self.__class__, - self.client.host(), - exc_info=self.client.last_except_txt(), - ) diff --git a/src/peak/properties.py b/src/peak/properties.py deleted file mode 100644 index a38cf99..0000000 --- a/src/peak/properties.py +++ /dev/null @@ -1,184 +0,0 @@ -# Standard library imports -from abc import ABCMeta, abstractmethod -from random import uniform -from typing import Dict, Iterable - -# Third party imports -from pandas import DataFrame - -# Reader imports -from peak.drivers import Driver - - -class Property: - """Represents a property of an agent. - - Attributes: - data: Array of objects. - loop: If True, when the property reaches the end of the data array - it comes back to the begining. - offset: Offset of the index of the data array. - random_range: Percentage that applies randomness to the data. - current_value: Current value of the property. - """ - - def __init__( - self, - data: Iterable, - loop: bool = False, - random_range: float = None, - offset: int = 0, - ): - self.data = data - self.loop = loop - self.offset = offset - self.random_range = random_range - self.iter = self._gen(data, loop, offset) - self.current_value = next(self.iter) - - def _gen(self, data: Iterable, loop: bool, offset: int): - """Retrieves values from the data iteratively. - - Args: - data: Array of objects. - loop: If True, when the property reaches the end of the data array - it comes back to the begining. - offset: Offset of the index of the data array. - - Yields: - The next value in the data array. - """ - f = True - while f: - try: - data = data[offset:] - for i in data: - if self.random_range: - yield i * uniform(1 - self.random_range, 1 + self.random_range) - else: - yield i - - if not loop: - while True: - yield None - except TypeError: - f = False - while True: - yield data - - def next(self): - """Iterates the data array.""" - self.current_value = next(self.iter) - - def __add__(self, other): - if isinstance(other, Property): - return self.current_value + other.current_value - if isinstance(other, (float, int)): - return self.current_value + other - raise TypeError - - def __sub__(self, other): - if isinstance(other, Property): - return float(self.current_value) - float(other.current_value) - if isinstance(other, (float, int)): - return self.current_value - other - raise TypeError - - def __mul__(self, other): - if isinstance(other, Property): - return self.current_value * other.current_value - if isinstance(other, (float, int)): - return self.current_value * other - raise TypeError - - def __div__(self, other): - if isinstance(other, Property): - return self.current_value / other.current_value - if isinstance(other, (float, int)): - return self.current_value / other - raise TypeError - - def __repr__(self) -> str: - return str(self.current_value) - - def __getitem__(self, item): - return self.data[(item % len(self.data)) - 1] - - -class Properties(metaclass=ABCMeta): - """Used to filtrate and process the data to be injected in the agent. - - Attributes: - agent_fullname: If the agent is a clone the full name will have - its original name the the clone number. If its not, the full - name will be equal to the name. - agent_name: Just the name without the clone number. - agent_number: The clone number if its a clone. - """ - - def __init__( - self, full_name: str = None, name: str = None, number: int = None - ) -> None: - self.agent_fullname: str = full_name - self.agent_name: str = name - self.agent_number: int = number - self.ds = dict() - self.build_dataset() - - def add_property(self, property_name: str, property: Property): - """Adds a single Property to the agent's properties. - - Args: - property_name: Name of the property. - property: An instance of Property. - """ - if self.agent_fullname not in self.ds: - self.ds[self.agent_fullname] = dict() - self.ds[self.agent_fullname][property_name] = property - - def add_dataset( - self, - dataframe: DataFrame, - loop: bool = False, - random_range: float = None, - offset: int = 0, - ): - """Adds a dataframe to the agent's properties. - - Args: - dataframe: Pandas Dataframe. - loop: If True, when the property reaches the end of the data array - it comes back to the begining. - random_range: Percentage that applies randomness to the data. - offset: Offset of the index of the data array. - """ - if self.agent_fullname not in self.ds: - self.ds[self.agent_fullname] = dict() - for property in dataframe: - self.ds[self.agent_fullname][property] = Property( - dataframe[property].to_list(), loop, random_range, offset - ) - - def add_driver(self, driver_name: str, driver: Driver): - """Adds a driver to the agent properties. - - Args: - driver_name: Name of the driver. - driver: The actual driver. - """ - if self.agent_fullname not in self.ds: - self.ds[self.agent_fullname] = dict() - self.ds[self.agent_fullname][driver_name] = driver - - @abstractmethod - def build_dataset(self): - """Contains the process of filtering and adding the data to the agent.""" - pass - - def extract(self) -> Dict[str, Property]: - """Extracts the dataset from this properties. - - Returns: - A dictionary with the agents properties. - """ - return self.ds[self.agent_fullname]