From db376012b420ec99e3085c695e63f7f344599596 Mon Sep 17 00:00:00 2001 From: FloSch62 Date: Wed, 12 Jun 2024 11:42:12 +0200 Subject: [PATCH 1/5] fix issue 16 --- .gitignore | 4 ++-- clab2drawio.py | 35 ++++++++++++++++++++++------------- styles/grafana_dark.yaml | 12 ++++++------ styles/nokia_bright.yaml | 15 ++++++++------- styles/nokia_dark.yaml | 10 +++++----- 5 files changed, 43 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index 9305a08..f24d6d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ venv/* __pycache__ -grafana_test/ -testlabsv2/ \ No newline at end of file +test/* +launch.json \ No newline at end of file diff --git a/clab2drawio.py b/clab2drawio.py index 9fa6c90..ffb37e0 100644 --- a/clab2drawio.py +++ b/clab2drawio.py @@ -1,4 +1,4 @@ -#from N2G import drawio_diagram +# from N2G import drawio_diagram from lib.CustomDrawioDiagram import CustomDrawioDiagram from lib.Link import Link from lib.Node import Node @@ -381,8 +381,8 @@ def calculate_positions(diagram, layout='vertical', verbose=False): nodes = sorted(nodes.values(), key=lambda node: (node.graph_level, node.name)) x_start, y_start = 100, 100 - padding_x, padding_y = 200, 200 - min_margin = 200 + padding_x, padding_y = 150, 175 + min_margin = 150 if verbose: print("Nodes before calculate_positions:", nodes) @@ -609,8 +609,6 @@ def set_graphlevel(node, current_graphlevel, visited=None): sorted_nodes = sorted(nodes.values(), key=lambda node: (node.graph_level, node.name)) return sorted_nodes - - def load_styles_from_config(config_path): try: with open(config_path, 'r') as file: @@ -624,23 +622,33 @@ def load_styles_from_config(config_path): print(error_message) exit() - # Initialize the styles dictionary with defaults and override with config values + # Parse the base style into a dictionary + base_style_dict = {item.split('=')[0]: item.split('=')[1] for item in config.get('base_style', '').split(';') if item} + + # Initialize styles dictionary with configuration values styles = { + 'background': config.get('background', "#FFFFFF"), + 'shadow': config.get('shadow', "1"), + 'grid': config.get('grid', "1"), + 'pagew': config.get('pagew', "827"), + 'pageh': config.get('pageh', "1169"), 'base_style': config.get('base_style', ''), 'link_style': config.get('link_style', ''), 'src_label_style': config.get('src_label_style', ''), 'trgt_label_style': config.get('trgt_label_style', ''), 'port_style': config.get('port_style', ''), - 'connector_style': config.get('connector_style', ''), - 'background': config.get('background', "#FFFFFF"), - 'shadow': config.get('shadow', "1"), - 'pagew': config.get('pagew', "827"), - 'pageh': config.get('pageh', "1169"), - 'grid': config.get('grid', "1"), - 'custom_styles': {key: config.get('base_style', '') + value for key, value in config.get('custom_styles', {}).items()}, + 'connector_style': config.get('connector_style', ''), 'icon_to_group_mapping': config.get('icon_to_group_mapping', {}), + 'custom_styles': {} } + # Merge base style with custom styles + for key, custom_style in config.get('custom_styles', {}).items(): + custom_style_dict = {item.split('=')[0]: item.split('=')[1] for item in custom_style.split(';') if item} + merged_style_dict = {**base_style_dict, **custom_style_dict} # custom style overrides base style + merged_style = ';'.join(f"{k}={v}" for k, v in merged_style_dict.items()) + styles['custom_styles'][key] = merged_style + # Read all other configuration values for key, value in config.items(): if key not in styles: @@ -648,6 +656,7 @@ def load_styles_from_config(config_path): return styles + def interactive_mode(nodes, icon_to_group_mapping, containerlab_data, output_file, processor): # Initialize previous summary with existing node labels previous_summary = {"Levels": {}, "Icons": {}} diff --git a/styles/grafana_dark.yaml b/styles/grafana_dark.yaml index 018f3c7..2b7128e 100644 --- a/styles/grafana_dark.yaml +++ b/styles/grafana_dark.yaml @@ -12,11 +12,11 @@ base_style: "shape=image;imageAlign=center;imageVerticalAlign=middle;labelPositi port_style: "ellipse;whiteSpace=wrap;html=1;aspect=fixed;fontColor=#FFFFFF;fontSize=6;strokeColor=#98A2AE;fillColor=#BEC8D2;" connector_style: "ellipse;whiteSpace=wrap;html=1;aspect=fixed;fontColor=#FFFFFF;fontSize=6;fillColor=#BEC8D2;strokeColor=none;noLabel=1;" -connector_width: 8 -connector_height: 8 +connector_width: 12 +connector_height: 12 # Style for links between nodes -link_style: "rounded=0;orthogonalLoop=1;html=1;startSize=6;endArrow=classicThin;endFill=1;endSize=2;fontSize=10;strokeColor=#98A2AE;fontColor=#FFFFFF;textOpacity=60;labelBackgroundColor=#4D5766;jumpStyle=gap;" +link_style: "rounded=0;orthogonalLoop=1;html=1;startSize=6;endArrow=classicThin;endFill=1;endSize=2;fontSize=14;strokeColor=#98A2AE;fontColor=#FFFFFF;textOpacity=60;labelBackgroundColor=#4D5766;jumpStyle=gap;strokeWidth=3;" # Styles for labels on the source and target ends of a link src_label_style: "edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=6;fontColor=#FFFFFF;textOpacity=60;labelBackgroundColor=#4D5766;" @@ -27,9 +27,9 @@ node_height: 75 # Custom styles for different types of nodes, allowing for unique visual representation based on node role or function custom_styles: - default: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/png,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;fontColor=#ffffff;labelPosition=left;align=right;" - spine: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/png,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;fontColor=#ffffff;labelPosition=left;align=right;" - leaf: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/png,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;fontColor=#ffffff;labelPosition=left;align=right;" + default: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" + spine: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" + leaf: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" dcgw: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIiB4PSIwIi8+JiN4YTs8Zz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTQ5LjcsNzBMMjAuMSw5OS44IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNOTcuNyw5Ny40TDY4LDY3LjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8L2c+JiN4YTsJPGc+JiN4YTsJCTxwYXRoIGQ9Ik03MC40LDQ5LjdMOTkuOSwyMCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8cGF0aCBkPSJNMjIuMywyMi4zTDUyLDUxLjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8cGF0aCBkPSJNMjAuMSwzMy45bDAtMTAuN2MwLTEuOCwxLjMtMywzLjEtMy4xbDEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik0zOC40LDY4bDEwLjcsMGMxLjgsMCwzLDEuMywzLjEsMy4xbDAsMTAuOCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik05OS44LDg2LjJsMCwxMC43YzAsMS44LTEuMywzLTMuMSwzLjFsLTEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik04MS44LDUxLjlsLTEwLjcsMGMtMS44LDAtMy0xLjMtMy4xLTMuMUw2OCwzOCIgY2xhc3M9InN0MSIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#ffffff;labelPosition=left;align=right;" server: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIi8+JiN4YTs8Zz4mI3hhOwk8cGF0aCBkPSJNMTAwLDkxLjFIMjBIMTAweiBNODkuMSwzMi41YzAtMC41LDAtMS0wLjItMS40Yy0wLjItMC41LTAuNC0wLjktMC44LTEuMmMtMC4zLTAuMy0wLjgtMC42LTEuMi0wLjgmIzEwOyYjOTsmIzk7Yy0wLjUtMC4yLTAuOS0wLjItMS40LTAuMkgzNC42Yy0wLjUsMC0xLDAtMS40LDAuMmMtMC41LDAuMi0wLjksMC40LTEuMiwwLjhjLTAuMywwLjMtMC42LDAuOC0wLjgsMS4yYy0wLjIsMC41LTAuMiwwLjktMC4yLDEuNCYjMTA7JiM5OyYjOTtWNzZoNTguMlYzMi41eiIgY2xhc3M9InN0NCIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#ffffff;labelPosition=left;align=right;" diff --git a/styles/nokia_bright.yaml b/styles/nokia_bright.yaml index 416c89b..56ac14f 100644 --- a/styles/nokia_bright.yaml +++ b/styles/nokia_bright.yaml @@ -14,8 +14,9 @@ base_style: "shape=image;imageAlign=center;imageVerticalAlign=middle;labelPositi #Style for grafana themes (only used if -g) port_style: "ellipse;whiteSpace=wrap;html=1;aspect=fixed;fontColor=#FFFFFF;fontSize=6;strokeColor=#98A2AE;fillColor=#BEC8D2;" connector_style: "ellipse;whiteSpace=wrap;html=1;aspect=fixed;fontColor=#FFFFFF;fontSize=6;fillColor=#BEC8D2;strokeColor=none;noLabel=1;" -connector_width: 8 -connector_height: 8 + +connector_width: 12 +connector_height: 12 # Style for links between nodes link_style: "endArrow=none;jumpStyle=gap;" @@ -26,11 +27,11 @@ trgt_label_style: "edgeLabel;html=1;align=center;verticalAlign=middle;resizable= # Custom styles for different types of nodes, allowing for unique visual representation based on node role or function custom_styles: - default: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/png,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;fontColor=#000000;labelPosition=left;align=right;" - spine: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/png,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;fontColor=#000000;labelPosition=left;align=right;" - leaf: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/png,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;fontColor=#000000;labelPosition=left;align=right;" - dcgw: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIiB4PSIwIi8+JiN4YTs8Zz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTQ5LjcsNzBMMjAuMSw5OS44IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNOTcuNyw5Ny40TDY4LDY3LjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8L2c+JiN4YTsJPGc+JiN4YTsJCTxwYXRoIGQ9Ik03MC40LDQ5LjdMOTkuOSwyMCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8cGF0aCBkPSJNMjIuMywyMi4zTDUyLDUxLjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8cGF0aCBkPSJNMjAuMSwzMy45bDAtMTAuN2MwLTEuOCwxLjMtMywzLjEtMy4xbDEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik0zOC40LDY4bDEwLjcsMGMxLjgsMCwzLDEuMywzLjEsMy4xbDAsMTAuOCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik05OS44LDg2LjJsMCwxMC43YzAsMS44LTEuMywzLTMuMSwzLjFsLTEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik04MS44LDUxLjlsLTEwLjcsMGMtMS44LDAtMy0xLjMtMy4xLTMuMUw2OCwzOCIgY2xhc3M9InN0MSIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#000000;labelPosition=left;align=right;" - server: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIi8+JiN4YTs8Zz4mI3hhOwk8cGF0aCBkPSJNMTAwLDkxLjFIMjBIMTAweiBNODkuMSwzMi41YzAtMC41LDAtMS0wLjItMS40Yy0wLjItMC41LTAuNC0wLjktMC44LTEuMmMtMC4zLTAuMy0wLjgtMC42LTEuMi0wLjgmIzEwOyYjOTsmIzk7Yy0wLjUtMC4yLTAuOS0wLjItMS40LTAuMkgzNC42Yy0wLjUsMC0xLDAtMS40LDAuMmMtMC41LDAuMi0wLjksMC40LTEuMiwwLjhjLTAuMywwLjMtMC42LDAuOC0wLjgsMS4yYy0wLjIsMC41LTAuMiwwLjktMC4yLDEuNCYjMTA7JiM5OyYjOTtWNzZoNTguMlYzMi41eiIgY2xhc3M9InN0NCIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#000000;labelPosition=left;align=right;" + default: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" + spine: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" + leaf: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" + dcgw: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIiB4PSIwIi8+JiN4YTs8Zz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTQ5LjcsNzBMMjAuMSw5OS44IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNOTcuNyw5Ny40TDY4LDY3LjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8L2c+JiN4YTsJPGc+JiN4YTsJCTxwYXRoIGQ9Ik03MC40LDQ5LjdMOTkuOSwyMCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8cGF0aCBkPSJNMjIuMywyMi4zTDUyLDUxLjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8cGF0aCBkPSJNMjAuMSwzMy45bDAtMTAuN2MwLTEuOCwxLjMtMywzLjEtMy4xbDEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik0zOC40LDY4bDEwLjcsMGMxLjgsMCwzLDEuMywzLjEsMy4xbDAsMTAuOCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik05OS44LDg2LjJsMCwxMC43YzAsMS44LTEuMywzLTMuMSwzLjFsLTEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik04MS44LDUxLjlsLTEwLjcsMGMtMS44LDAtMy0xLjMtMy4xLTMuMUw2OCwzOCIgY2xhc3M9InN0MSIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#ffffff;labelPosition=left;align=right;" + server: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIi8+JiN4YTs8Zz4mI3hhOwk8cGF0aCBkPSJNMTAwLDkxLjFIMjBIMTAweiBNODkuMSwzMi41YzAtMC41LDAtMS0wLjItMS40Yy0wLjItMC41LTAuNC0wLjktMC44LTEuMmMtMC4zLTAuMy0wLjgtMC42LTEuMi0wLjgmIzEwOyYjOTsmIzk7Yy0wLjUtMC4yLTAuOS0wLjItMS40LTAuMkgzNC42Yy0wLjUsMC0xLDAtMS40LDAuMmMtMC41LDAuMi0wLjksMC40LTEuMiwwLjhjLTAuMywwLjMtMC42LDAuOC0wLjgsMS4yYy0wLjIsMC41LTAuMiwwLjktMC4yLDEuNCYjMTA7JiM5OyYjOTtWNzZoNTguMlYzMi41eiIgY2xhc3M9InN0NCIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#ffffff;labelPosition=left;align=right;" # icon_to_group_mapping defines the mapping between 'graph-icon' labels specified in the Containerlab file and the custom_styles keys. # 'graph-icon' is used within the Containerlab YAML configuration under 'labels' to visually differentiate nodes by type in the generated diagram. diff --git a/styles/nokia_dark.yaml b/styles/nokia_dark.yaml index fa73447..f7bf926 100644 --- a/styles/nokia_dark.yaml +++ b/styles/nokia_dark.yaml @@ -14,8 +14,8 @@ base_style: "shape=image;imageAlign=center;imageVerticalAlign=middle;labelPositi #Style for grafana themes (only used if -g) port_style: "ellipse;whiteSpace=wrap;html=1;aspect=fixed;fontColor=#FFFFFF;fontSize=6;strokeColor=#98A2AE;fillColor=#BEC8D2;" connector_style: "ellipse;whiteSpace=wrap;html=1;aspect=fixed;fontColor=#FFFFFF;fontSize=6;fillColor=#BEC8D2;strokeColor=none;noLabel=1;" -connector_width: 8 -connector_height: 8 +connector_width: 12 +connector_height: 12 # Style for links between nodes @@ -27,9 +27,9 @@ trgt_label_style: "edgeLabel;html=1;align=center;verticalAlign=middle;resizable= # Custom styles for different types of nodes, allowing for unique visual representation based on node role or function custom_styles: - default: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/png,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;fontColor=#ffffff;labelPosition=left;align=right;" - spine: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/png,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;fontColor=#ffffff;labelPosition=left;align=right;" - leaf: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/png,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;fontColor=#ffffff;labelPosition=left;align=right;" + default: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" + spine: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" + leaf: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" dcgw: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIiB4PSIwIi8+JiN4YTs8Zz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTQ5LjcsNzBMMjAuMSw5OS44IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNOTcuNyw5Ny40TDY4LDY3LjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8L2c+JiN4YTsJPGc+JiN4YTsJCTxwYXRoIGQ9Ik03MC40LDQ5LjdMOTkuOSwyMCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8cGF0aCBkPSJNMjIuMywyMi4zTDUyLDUxLjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8cGF0aCBkPSJNMjAuMSwzMy45bDAtMTAuN2MwLTEuOCwxLjMtMywzLjEtMy4xbDEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik0zOC40LDY4bDEwLjcsMGMxLjgsMCwzLDEuMywzLjEsMy4xbDAsMTAuOCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik05OS44LDg2LjJsMCwxMC43YzAsMS44LTEuMywzLTMuMSwzLjFsLTEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik04MS44LDUxLjlsLTEwLjcsMGMtMS44LDAtMy0xLjMtMy4xLTMuMUw2OCwzOCIgY2xhc3M9InN0MSIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#ffffff;labelPosition=left;align=right;" server: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIi8+JiN4YTs8Zz4mI3hhOwk8cGF0aCBkPSJNMTAwLDkxLjFIMjBIMTAweiBNODkuMSwzMi41YzAtMC41LDAtMS0wLjItMS40Yy0wLjItMC41LTAuNC0wLjktMC44LTEuMmMtMC4zLTAuMy0wLjgtMC42LTEuMi0wLjgmIzEwOyYjOTsmIzk7Yy0wLjUtMC4yLTAuOS0wLjItMS40LTAuMkgzNC42Yy0wLjUsMC0xLDAtMS40LDAuMmMtMC41LDAuMi0wLjksMC40LTEuMiwwLjhjLTAuMywwLjMtMC42LDAuOC0wLjgsMS4yYy0wLjIsMC41LTAuMiwwLjktMC4yLDEuNCYjMTA7JiM5OyYjOTtWNzZoNTguMlYzMi41eiIgY2xhc3M9InN0NCIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#ffffff;labelPosition=left;align=right;" From e08b8383c83cbff011d9e661aae3708a5bc4ec9a Mon Sep 17 00:00:00 2001 From: FloSch62 Date: Wed, 12 Jun 2024 11:47:38 +0200 Subject: [PATCH 2/5] Made the linting happy --- clab2drawio.py | 710 ++++++++++++++++++++++++------------- drawio2clab.py | 283 +++++++++------ lib/CustomDrawioDiagram.py | 111 +++--- lib/Grafana.py | 69 ++-- lib/Link.py | 40 +-- lib/Node.py | 66 ++-- lib/Yaml_processor.py | 25 +- 7 files changed, 840 insertions(+), 464 deletions(-) diff --git a/clab2drawio.py b/clab2drawio.py index ffb37e0..49f5bce 100644 --- a/clab2drawio.py +++ b/clab2drawio.py @@ -6,7 +6,12 @@ from lib.Yaml_processor import YAMLProcessor from collections import defaultdict from prompt_toolkit.shortcuts import checkboxlist_dialog, yes_no_dialog -import yaml, argparse, os, re, random +import yaml +import argparse +import os +import re +import random + def add_ports(diagram, styles, verbose=True): nodes = diagram.nodes @@ -24,67 +29,91 @@ def add_ports(diagram, styles, verbose=True): direction_groups[direction].append(link) for direction, group in direction_groups.items(): - if diagram.layout == 'vertical': - if direction == 'downstream': + if diagram.layout == "vertical": + if direction == "downstream": # Sort downstream links by x position of source and target - sorted_links = sorted(group, key=lambda link: (link.source.pos_x, link.target.pos_x)) + sorted_links = sorted( + group, key=lambda link: (link.source.pos_x, link.target.pos_x) + ) num_links = len(sorted_links) - spacing = styles['node_width'] / (num_links + 1) + spacing = styles["node_width"] / (num_links + 1) for i, link in enumerate(sorted_links): - port_x = node.pos_x + (i + 1) * spacing - styles['connector_width'] / 2 - port_y = node.pos_y + styles['node_height'] - styles['connector_height'] / 2 + port_x = ( + node.pos_x + + (i + 1) * spacing + - styles["connector_width"] / 2 + ) + port_y = ( + node.pos_y + + styles["node_height"] + - styles["connector_height"] / 2 + ) link.port_pos = (port_x, port_y) - elif direction == 'upstream': + elif direction == "upstream": # Sort upstream links by x position of source and target - sorted_links = sorted(group, key=lambda link: (link.source.pos_x, link.target.pos_x)) + sorted_links = sorted( + group, key=lambda link: (link.source.pos_x, link.target.pos_x) + ) num_links = len(sorted_links) - spacing = styles['node_width'] / (num_links + 1) + spacing = styles["node_width"] / (num_links + 1) for i, link in enumerate(sorted_links): - port_x = node.pos_x + (i + 1) * spacing - styles['connector_width'] / 2 - port_y = node.pos_y - styles['connector_height'] / 2 + port_x = ( + node.pos_x + + (i + 1) * spacing + - styles["connector_width"] / 2 + ) + port_y = node.pos_y - styles["connector_height"] / 2 link.port_pos = (port_x, port_y) else: # Sort lateral links by y position of source and target - sorted_links = sorted(group, key=lambda link: (link.source.pos_y, link.target.pos_y)) + sorted_links = sorted( + group, key=lambda link: (link.source.pos_y, link.target.pos_y) + ) num_links = len(sorted_links) - spacing = styles['node_height'] / (num_links + 1) + spacing = styles["node_height"] / (num_links + 1) for i, link in enumerate(sorted_links): if link.target.pos_x > link.source.pos_x: # Lateral link to the right - port_x = node.pos_x + styles['node_width'] + port_x = node.pos_x + styles["node_width"] else: # Lateral link to the left port_x = node.pos_x port_y = node.pos_y + (i + 1) * spacing link.port_pos = (port_x, port_y) - elif diagram.layout == 'horizontal': - if direction == 'downstream': + elif diagram.layout == "horizontal": + if direction == "downstream": # Sort downstream links by y position of source and target - sorted_links = sorted(group, key=lambda link: (link.source.pos_y, link.target.pos_y)) + sorted_links = sorted( + group, key=lambda link: (link.source.pos_y, link.target.pos_y) + ) num_links = len(sorted_links) - spacing = styles['node_height'] / (num_links + 1) + spacing = styles["node_height"] / (num_links + 1) for i, link in enumerate(sorted_links): - port_x = node.pos_x + styles['node_width'] + port_x = node.pos_x + styles["node_width"] port_y = node.pos_y + (i + 1) * spacing link.port_pos = (port_x, port_y) - elif direction == 'upstream': + elif direction == "upstream": # Sort upstream links by y position of source and target - sorted_links = sorted(group, key=lambda link: (link.source.pos_y, link.target.pos_y)) + sorted_links = sorted( + group, key=lambda link: (link.source.pos_y, link.target.pos_y) + ) num_links = len(sorted_links) - spacing = styles['node_height'] / (num_links + 1) + spacing = styles["node_height"] / (num_links + 1) for i, link in enumerate(sorted_links): port_x = node.pos_x port_y = node.pos_y + (i + 1) * spacing link.port_pos = (port_x, port_y) else: # Sort lateral links by x position of source and target - sorted_links = sorted(group, key=lambda link: (link.source.pos_x, link.target.pos_x)) + sorted_links = sorted( + group, key=lambda link: (link.source.pos_x, link.target.pos_x) + ) num_links = len(sorted_links) - spacing = styles['node_width'] / (num_links + 1) + spacing = styles["node_width"] / (num_links + 1) for i, link in enumerate(sorted_links): if link.target.pos_y > link.source.pos_y: # Lateral link to the bottom - port_y = node.pos_y + styles['node_height'] + port_y = node.pos_y + styles["node_height"] else: # Lateral link to the top port_y = node.pos_y @@ -95,23 +124,27 @@ def add_ports(diagram, styles, verbose=True): # Create connectors and links using the calculated port positions processed_connections = set() for node in nodes.values(): - downstream_links = node.get_downstream_links() lateral_links = node.get_lateral_links() links = downstream_links + lateral_links - + for link in links: - connection_id = frozenset({(link.source.name, link.source_intf), (link.target.name, link.target_intf)}) + connection_id = frozenset( + { + (link.source.name, link.source_intf), + (link.target.name, link.target_intf), + } + ) if connection_id not in processed_connections: processed_connections.add(connection_id) - #print(connection_id) + # print(connection_id) # source connector source_cID = f"{link.source.name}:{link.source_intf}:{link.target.name}:{link.target_intf}" - source_label = re.findall(r'\d+', link.source_intf)[-1] + source_label = re.findall(r"\d+", link.source_intf)[-1] source_connector_pos = link.port_pos - connector_width = styles['connector_width'] - connector_height = styles['connector_height'] + connector_width = styles["connector_width"] + connector_height = styles["connector_height"] # Add the source connector ID to the source connector dictionary if link.source.name not in connector_dict: @@ -122,7 +155,7 @@ def add_ports(diagram, styles, verbose=True): target_cID = f"{link.target.name}:{link.target_intf}:{link.source.name}:{link.source_intf}" target_link = diagram.get_target_link(link) target_connector_pos = target_link.port_pos - target_label = re.findall(r'\d+', target_link.source_intf)[-1] + target_label = re.findall(r"\d+", target_link.source_intf)[-1] if link.target.name not in connector_dict: connector_dict[link.target.name] = [] @@ -131,30 +164,45 @@ def add_ports(diagram, styles, verbose=True): # Adjust port positions if source and target have different numbers of links source_downstream_links = link.source.get_downstream_links() target_upstream_links = link.target.get_upstream_links() - if diagram.layout == 'vertical': + if diagram.layout == "vertical": if link.source.pos_x == link.target.pos_x: if len(source_downstream_links) != len(target_upstream_links): - if len(source_downstream_links) < len(target_upstream_links): + if len(source_downstream_links) < len( + target_upstream_links + ): # Adjust source port position to align with the corresponding target port adjusted_x = target_connector_pos[0] - source_connector_pos = (adjusted_x, source_connector_pos[1]) + source_connector_pos = ( + adjusted_x, + source_connector_pos[1], + ) else: # Adjust target port position to align with the corresponding source port adjusted_x = source_connector_pos[0] - target_connector_pos = (adjusted_x, target_connector_pos[1]) - elif diagram.layout == 'horizontal': + target_connector_pos = ( + adjusted_x, + target_connector_pos[1], + ) + elif diagram.layout == "horizontal": if link.source.pos_y == link.target.pos_y: - #pass + # pass if len(source_downstream_links) != len(target_upstream_links): - if len(source_downstream_links) < len(target_upstream_links): + if len(source_downstream_links) < len( + target_upstream_links + ): # Adjust source port position to align with the corresponding target port adjusted_y = target_connector_pos[1] - source_connector_pos = (source_connector_pos[0], adjusted_y) + source_connector_pos = ( + source_connector_pos[0], + adjusted_y, + ) else: # Adjust target port position to align with the corresponding source port adjusted_y = source_connector_pos[1] - target_connector_pos = (target_connector_pos[0], adjusted_y) - + target_connector_pos = ( + target_connector_pos[0], + adjusted_y, + ) diagram.add_node( id=source_cID, @@ -163,7 +211,7 @@ def add_ports(diagram, styles, verbose=True): y_pos=source_connector_pos[1], width=connector_width, height=connector_height, - style=styles['port_style'] + style=styles["port_style"], ) diagram.add_node( @@ -173,26 +221,34 @@ def add_ports(diagram, styles, verbose=True): y_pos=target_connector_pos[1], width=connector_width, height=connector_height, - style=styles['port_style'] + style=styles["port_style"], ) # Calculate center positions - source_center = (source_connector_pos[0] + connector_width / 2, source_connector_pos[1] + connector_height / 2) - target_center = (target_connector_pos[0] + connector_width / 2, target_connector_pos[1] + connector_height / 2) + source_center = ( + source_connector_pos[0] + connector_width / 2, + source_connector_pos[1] + connector_height / 2, + ) + target_center = ( + target_connector_pos[0] + connector_width / 2, + target_connector_pos[1] + connector_height / 2, + ) # Calculate the real middle between the centers for the midpoint connector midpoint_center_x = (source_center[0] + target_center[0]) / 2 midpoint_center_y = (source_center[1] + target_center[1]) / 2 # Generate a random offset within the range of ±10 - random_offset = random.choice([random.uniform(-20, -10), random.uniform(10, 20)]) + random_offset = random.choice( + [random.uniform(-20, -10), random.uniform(10, 20)] + ) # Determine the direction of the link dx = target_center[0] - source_center[0] dy = target_center[1] - source_center[1] # Calculate the normalized direction vector for the line - magnitude = (dx**2 + dy**2)**0.5 + magnitude = (dx**2 + dy**2) ** 0.5 if magnitude != 0: direction_dx = dx / magnitude direction_dy = dy / magnitude @@ -213,23 +269,37 @@ def add_ports(diagram, styles, verbose=True): midpoint_id = f"mid:{link.source.name}:{link.source_intf}:{link.target.name}:{link.target_intf}" diagram.add_node( id=midpoint_id, - label='\u200B', + label="\u200b", x_pos=midpoint_top_left_x, y_pos=midpoint_top_left_y, width=4, height=4, - style=styles['connector_style'] + style=styles["connector_style"], ) - diagram.add_link(source=source_cID, target=midpoint_id, style=styles["link_style"], label='\u200B', link_id=f"{source_cID}") - diagram.add_link(source=target_cID, target=midpoint_id, style=styles["link_style"], label='\u200B', link_id=f"{target_cID}") - + diagram.add_link( + source=source_cID, + target=midpoint_id, + style=styles["link_style"], + label="\u200b", + link_id=f"{source_cID}", + ) + diagram.add_link( + source=target_cID, + target=midpoint_id, + style=styles["link_style"], + label="\u200b", + link_id=f"{target_cID}", + ) - # Create groups for each node and its connectors + # Create groups for each node and its connectors for node_name, connector_ids in connector_dict.items(): group_id = f"group-{node_name}" member_objects = connector_ids + [node_name] - diagram.group_nodes(member_objects=member_objects, group_id=group_id, style='group') + diagram.group_nodes( + member_objects=member_objects, group_id=group_id, style="group" + ) + def add_links(diagram, styles): nodes = diagram.nodes @@ -260,7 +330,7 @@ def add_links(diagram, styles): # Calculate step for multiple links with the same target step = 0.5 if len(group) == 1 else 0.25 + 0.5 * (i / (len(group) - 1)) - if diagram.layout == 'horizontal': + if diagram.layout == "horizontal": if link.level_diff > 0: entryX, exitX = (0, 1) if left_to_right else (1, 0) entryY = exitY = step @@ -270,7 +340,7 @@ def add_links(diagram, styles): else: entryY, exitY = (1, 0) entryX = exitX = step - elif diagram.layout == 'vertical': + elif diagram.layout == "vertical": if link.level_diff > 0: entryY, exitY = (0, 1) if above_to_below else (1, 0) entryX = exitX = step @@ -283,13 +353,21 @@ def add_links(diagram, styles): entryY = exitY = step style = f"{styles['link_style']}entryY={entryY};exitY={exitY};entryX={entryX};exitX={exitX};" - diagram.add_link(source=link.source.name, target=link.target.name, src_label=link.source_intf, trgt_label=link.target_intf, src_label_style=styles['src_label_style'], trgt_label_style=styles['trgt_label_style'], style=style) + diagram.add_link( + source=link.source.name, + target=link.target.name, + src_label=link.source_intf, + trgt_label=link.target_intf, + src_label_style=styles["src_label_style"], + trgt_label_style=styles["trgt_label_style"], + style=style, + ) def add_nodes(diagram, nodes, styles): - base_style = styles['base_style'] - custom_styles = styles['custom_styles'] - icon_to_group_mapping = styles['icon_to_group_mapping'] + base_style = styles["base_style"] + custom_styles = styles["custom_styles"] + icon_to_group_mapping = styles["icon_to_group_mapping"] for node in nodes.values(): # Check for 'graph_icon' attribute and map it to the corresponding group @@ -306,71 +384,90 @@ def add_nodes(diagram, nodes, styles): elif "dcgw" in node.name: group = "dcgw" else: - group = "default" # Fallback to 'default' if none of the conditions are met + group = ( + "default" # Fallback to 'default' if none of the conditions are met + ) style = custom_styles.get(group, base_style) x_pos, y_pos = node.pos_x, node.pos_y # Add each node to the diagram with the given x and y position. - diagram.add_node(id=node.name, label=node.label, x_pos=x_pos, y_pos=y_pos, style=style, width=node.width, height=node.height) + diagram.add_node( + id=node.name, + label=node.label, + x_pos=x_pos, + y_pos=y_pos, + style=style, + width=node.width, + height=node.height, + ) -def adjust_intermediary_nodes(intermediaries, layout, verbose=False): +def adjust_intermediary_nodes(intermediaries, layout, verbose=False): if not intermediaries: return - #group the intermediaries by their graph level + # group the intermediaries by their graph level intermediaries_by_level = defaultdict(list) for node in intermediaries: intermediaries_by_level[node.graph_level].append(node) - selected_level = max(intermediaries_by_level.keys(), key=lambda lvl: len(intermediaries_by_level[lvl])) - selected_group = intermediaries_by_level[selected_level] + selected_level = max( + intermediaries_by_level.keys(), + key=lambda lvl: len(intermediaries_by_level[lvl]), + ) + selected_group = intermediaries_by_level[selected_level] if len(selected_group) == 1: node = selected_group[0] - if layout == 'vertical': + if layout == "vertical": node.pos_x = node.pos_x - 100 else: node.pos_y = node.pos_y - 100 else: for i, node in enumerate(selected_group): - if layout == 'vertical': + if layout == "vertical": node.pos_x = node.pos_x - 100 + i * 200 else: node.pos_y = node.pos_y - 100 + i * 200 pass -def center_align_nodes(nodes_by_graphlevel, layout='vertical', verbose=False): + +def center_align_nodes(nodes_by_graphlevel, layout="vertical", verbose=False): """ Center align nodes within each graphlevel based on the layout layout and ensure they are nicely distributed to align with the graphlevel above. """ - - attr_x, attr_y = ('pos_x', 'pos_y') if layout == 'vertical' else ('pos_y', 'pos_x') - + + attr_x, attr_y = ("pos_x", "pos_y") if layout == "vertical" else ("pos_y", "pos_x") + prev_graphlevel_center = None for graphlevel, nodes in sorted(nodes_by_graphlevel.items()): graphlevel_centers = [getattr(node, attr_x) for node in nodes] - + if prev_graphlevel_center is None: # For the first graphlevel, calculate its center and use it as the previous center for the next level - prev_graphlevel_center = (min(graphlevel_centers) + max(graphlevel_centers)) / 2 + prev_graphlevel_center = ( + min(graphlevel_centers) + max(graphlevel_centers) + ) / 2 else: # Calculate current graphlevel's center graphlevel_center = sum(graphlevel_centers) / len(nodes) - + # Calculate offset to align current graphlevel's center with the previous graphlevel's center offset = prev_graphlevel_center - graphlevel_center - + # Apply offset to each node in the current graphlevel for node in nodes: setattr(node, attr_x, getattr(node, attr_x) + offset) - + # Update prev_graphlevel_center for the next level - prev_graphlevel_center = sum(getattr(node, attr_x) for node in nodes) / len(nodes) + prev_graphlevel_center = sum(getattr(node, attr_x) for node in nodes) / len( + nodes + ) + -def calculate_positions(diagram, layout='vertical', verbose=False): +def calculate_positions(diagram, layout="vertical", verbose=False): """ Calculates and assigns positions to nodes for graph visualization based on their hierarchical levels and connectivity. Organizes nodes by graph level, applies prioritization within levels based on connectivity, and adjusts positions to enhance readability. @@ -378,7 +475,7 @@ def calculate_positions(diagram, layout='vertical', verbose=False): """ nodes = diagram.nodes - nodes = sorted(nodes.values(), key=lambda node: (node.graph_level, node.name)) + nodes = sorted(nodes.values(), key=lambda node: (node.graph_level, node.name)) x_start, y_start = 100, 100 padding_x, padding_y = 150, 175 @@ -389,19 +486,28 @@ def calculate_positions(diagram, layout='vertical', verbose=False): def prioritize_placement(nodes, level, verbose=False): if level == diagram.get_max_level(): - # If it's the maximum level, simply sort nodes by name + # If it's the maximum level, simply sort nodes by name ordered_nodes = sorted(nodes, key=lambda node: node.name) - else: - # Separate nodes by their connection count within the level - multi_connection_nodes = [node for node in nodes if node.get_connection_count_within_level() > 1] - single_connection_nodes = [node for node in nodes if node.get_connection_count_within_level() == 1] - zero_connection_nodes = [node for node in nodes if node.get_connection_count_within_level() == 0] + else: + # Separate nodes by their connection count within the level + multi_connection_nodes = [ + node for node in nodes if node.get_connection_count_within_level() > 1 + ] + single_connection_nodes = [ + node for node in nodes if node.get_connection_count_within_level() == 1 + ] + zero_connection_nodes = [ + node for node in nodes if node.get_connection_count_within_level() == 0 + ] # Separate multi-connection nodes with lateral links multi_connection_nodes_with_lateral = [] multi_connection_nodes_without_lateral = [] for node in multi_connection_nodes: - if any(link.target in multi_connection_nodes for link in node.get_lateral_links()): + if any( + link.target in multi_connection_nodes + for link in node.get_lateral_links() + ): multi_connection_nodes_with_lateral.append(node) else: multi_connection_nodes_without_lateral.append(node) @@ -417,17 +523,24 @@ def prioritize_placement(nodes, level, verbose=False): sorted_multi_connection_nodes_with_lateral.append(link.target) # sort by name - multi_connection_nodes_without_lateral = sorted(multi_connection_nodes_without_lateral, key=lambda node: node.name) - sorted_multi_connection_nodes_with_lateral = sorted(sorted_multi_connection_nodes_with_lateral, key=lambda node: node.name) - single_connection_nodes = sorted(single_connection_nodes, key=lambda node: node.name) - + multi_connection_nodes_without_lateral = sorted( + multi_connection_nodes_without_lateral, key=lambda node: node.name + ) + sorted_multi_connection_nodes_with_lateral = sorted( + sorted_multi_connection_nodes_with_lateral, key=lambda node: node.name + ) + single_connection_nodes = sorted( + single_connection_nodes, key=lambda node: node.name + ) # Merge single, multi-connection (with and without lateral), and zero-connection nodes - ordered_nodes = single_connection_nodes[:len(single_connection_nodes)//2] + \ - multi_connection_nodes_without_lateral + \ - sorted_multi_connection_nodes_with_lateral + \ - single_connection_nodes[len(single_connection_nodes)//2:] + \ - zero_connection_nodes + ordered_nodes = ( + single_connection_nodes[: len(single_connection_nodes) // 2] + + multi_connection_nodes_without_lateral + + sorted_multi_connection_nodes_with_lateral + + single_connection_nodes[len(single_connection_nodes) // 2 :] + + zero_connection_nodes + ) return ordered_nodes @@ -437,10 +550,12 @@ def prioritize_placement(nodes, level, verbose=False): nodes_by_graphlevel[node.graph_level].append(node) for graphlevel, graphlevel_nodes in nodes_by_graphlevel.items(): - ordered_nodes = prioritize_placement(graphlevel_nodes, graphlevel, verbose=verbose) - + ordered_nodes = prioritize_placement( + graphlevel_nodes, graphlevel, verbose=verbose + ) + for i, node in enumerate(ordered_nodes): - if layout == 'vertical': + if layout == "vertical": node.pos_x = x_start + i * padding_x node.pos_y = y_start + graphlevel * padding_y else: @@ -449,76 +564,81 @@ def prioritize_placement(nodes, level, verbose=False): center_align_nodes(nodes_by_graphlevel, layout=layout, verbose=verbose) - intermediaries_x, intermediaries_y = diagram.get_nodes_between_interconnected() + intermediaries_x, intermediaries_y = diagram.get_nodes_between_interconnected() if diagram.layout == "vertical": - adjust_intermediary_nodes(intermediaries_x, layout=diagram.layout, verbose=verbose) + adjust_intermediary_nodes( + intermediaries_x, layout=diagram.layout, verbose=verbose + ) else: - adjust_intermediary_nodes(intermediaries_y, layout=diagram.layout, verbose=verbose) + adjust_intermediary_nodes( + intermediaries_y, layout=diagram.layout, verbose=verbose + ) + def adjust_node_levels(diagram): used_levels = diagram.get_used_levels() max_level = diagram.get_max_level() min_level = diagram.get_min_level() - #print(f"Initial used levels: {used_levels}") + # print(f"Initial used levels: {used_levels}") if len(used_levels) <= 1: - #print("Only one level present, no adjustment needed.") + # print("Only one level present, no adjustment needed.") return # Only one level present, no adjustment needed current_level = min_level while current_level < max_level + 1: - #if level is the first used level or the last used level, skip it + # if level is the first used level or the last used level, skip it if current_level == min_level: - #print(f"Skip Level: {current_level} because it is the first or last level") + # print(f"Skip Level: {current_level} because it is the first or last level") current_level += 1 continue nodes_at_current_level = diagram.get_nodes_by_level(current_level) nodes_at_next_level = diagram.get_nodes_by_level(current_level + 1) - #print(f"Processing level {current_level}:") - #print(f"Nodes at current level: {{current_level}} {[node.name for node in nodes_at_current_level.values()]}") + # print(f"Processing level {current_level}:") + # print(f"Nodes at current level: {{current_level}} {[node.name for node in nodes_at_current_level.values()]}") next_level = current_level + 1 before_level = current_level - 1 nodes_to_move = [] # if nodes_at_next_level: if len(nodes_at_current_level.items()) == 1: - #print(f"Only one node found at level {current_level}. No adjustment needed.") + # print(f"Only one node found at level {current_level}. No adjustment needed.") current_level += 1 continue - for node_name , node in nodes_at_current_level.items(): - has_upstream_connection = any(node.get_upstream_links_towards_level(before_level)) + for node_name, node in nodes_at_current_level.items(): + has_upstream_connection = any( + node.get_upstream_links_towards_level(before_level) + ) - if not has_upstream_connection: nodes_to_move.append(node) - #else: - #print(f"Node {node_name} has {len(node.get_upstream_links_towards_level(before_level))} upstream links against Level {before_level} No adjustment needed.") + # else: + # print(f"Node {node_name} has {len(node.get_upstream_links_towards_level(before_level))} upstream links against Level {before_level} No adjustment needed.") - if (len(nodes_to_move) == len(nodes_at_current_level) ): - #print(f"Nothing to move here") + if len(nodes_to_move) == len(nodes_at_current_level): + # print(f"Nothing to move here") current_level += 1 continue - #else: - #for node in nodes_to_move: - #print(f"!Node {node.name} does not have an upstream connection to level {before_level}. Marked for movement.") - + # else: + # for node in nodes_to_move: + # print(f"!Node {node.name} does not have an upstream connection to level {before_level}. Marked for movement.") if nodes_to_move: - #print(f"Because we need to move, we are increasing all node_graphlevels from the next Levels Nodes by one level") + # print(f"Because we need to move, we are increasing all node_graphlevels from the next Levels Nodes by one level") for level in range(max_level, current_level, -1): nodes_at_level = diagram.get_nodes_by_level(level) for node in nodes_at_level.values(): node.graph_level += 1 - #print(f" Moving node {node.name} from level {level} to level {level + 1}.") + # print(f" Moving node {node.name} from level {level} to level {level + 1}.") # Move the nodes marked for movement to the next level for node in nodes_to_move: node.graph_level += 1 - #print(f" Moving node {node.name} from level {current_level} to level {next_level}") + # print(f" Moving node {node.name} from level {current_level} to level {next_level}") - #print(f"Moved nodes at level {current_level} to level {next_level}.") + # print(f"Moved nodes at level {current_level} to level {next_level}.") update_links(diagram.get_links_from_nodes()) max_level = diagram.get_max_level() @@ -536,28 +656,30 @@ def adjust_node_levels(diagram): if level_diff == 1: can_move = False break # Stop checking if any upstream link has a level difference of 1 - + if can_move: for link in upstream_links: level_diff = node.graph_level - link.target.graph_level if level_diff > 1: node.graph_level -= 1 - #print(f" Moving node {node.name} from level {level} to level {level - 1} due to upstream link with level difference > 1") + # print(f" Moving node {node.name} from level {level} to level {level - 1} due to upstream link with level difference > 1") update_links(diagram.get_links_from_nodes()) max_level = diagram.get_max_level() break # Stop moving the node after adjusting its level once + def update_links(links): for link in links: source_level = link.source.graph_level target_level = link.target.graph_level link.level_diff = target_level - source_level if link.level_diff > 0: - link.direction = 'downstream' + link.direction = "downstream" elif link.level_diff < 0: - link.direction = 'upstream' + link.direction = "upstream" else: - link.direction = 'lateral' + link.direction = "lateral" + def assign_graphlevels(diagram, verbose=False): """ @@ -566,12 +688,14 @@ def assign_graphlevels(diagram, verbose=False): """ nodes = diagram.get_nodes() - # Check if all nodes already have a graphlevel != -1 + # Check if all nodes already have a graphlevel != -1 if all(node.graph_level != -1 for node in nodes.values()): already_set = True else: already_set = False - print("Not all graph levels set in the .clab file. Assigning graph levels based on downstream links. Expect experimental output. Please consider assigning graph levels to your .clab file, or use it with -I for interactive mode. Find more information here: https://github.com/srl-labs/clab-io-draw/blob/grafana_style/docs/clab2drawio.md#influencing-node-placement") + print( + "Not all graph levels set in the .clab file. Assigning graph levels based on downstream links. Expect experimental output. Please consider assigning graph levels to your .clab file, or use it with -I for interactive mode. Find more information here: https://github.com/srl-labs/clab-io-draw/blob/grafana_style/docs/clab2drawio.md#influencing-node-placement" + ) # Helper function to assign graphlevel by recursively checking connections def set_graphlevel(node, current_graphlevel, visited=None): @@ -587,7 +711,7 @@ def set_graphlevel(node, current_graphlevel, visited=None): target_node = nodes[link.target.name] set_graphlevel(target_node, current_graphlevel + 1, visited) - # Start by setting the graphlevel to -1 if they don't already have a graphlevel + # Start by setting the graphlevel to -1 if they don't already have a graphlevel for node in nodes.values(): if node.graph_level != -1: continue @@ -606,15 +730,20 @@ def set_graphlevel(node, current_graphlevel, visited=None): for node in nodes.values(): node.update_links() - sorted_nodes = sorted(nodes.values(), key=lambda node: (node.graph_level, node.name)) + sorted_nodes = sorted( + nodes.values(), key=lambda node: (node.graph_level, node.name) + ) return sorted_nodes + def load_styles_from_config(config_path): try: - with open(config_path, 'r') as file: + with open(config_path, "r") as file: config = yaml.safe_load(file) except FileNotFoundError: - error_message = f"Error: The specified config file '{config_path}' does not exist." + error_message = ( + f"Error: The specified config file '{config_path}' does not exist." + ) print(error_message) exit() except Exception as e: @@ -623,31 +752,42 @@ def load_styles_from_config(config_path): exit() # Parse the base style into a dictionary - base_style_dict = {item.split('=')[0]: item.split('=')[1] for item in config.get('base_style', '').split(';') if item} + base_style_dict = { + item.split("=")[0]: item.split("=")[1] + for item in config.get("base_style", "").split(";") + if item + } # Initialize styles dictionary with configuration values styles = { - 'background': config.get('background', "#FFFFFF"), - 'shadow': config.get('shadow', "1"), - 'grid': config.get('grid', "1"), - 'pagew': config.get('pagew', "827"), - 'pageh': config.get('pageh', "1169"), - 'base_style': config.get('base_style', ''), - 'link_style': config.get('link_style', ''), - 'src_label_style': config.get('src_label_style', ''), - 'trgt_label_style': config.get('trgt_label_style', ''), - 'port_style': config.get('port_style', ''), - 'connector_style': config.get('connector_style', ''), - 'icon_to_group_mapping': config.get('icon_to_group_mapping', {}), - 'custom_styles': {} + "background": config.get("background", "#FFFFFF"), + "shadow": config.get("shadow", "1"), + "grid": config.get("grid", "1"), + "pagew": config.get("pagew", "827"), + "pageh": config.get("pageh", "1169"), + "base_style": config.get("base_style", ""), + "link_style": config.get("link_style", ""), + "src_label_style": config.get("src_label_style", ""), + "trgt_label_style": config.get("trgt_label_style", ""), + "port_style": config.get("port_style", ""), + "connector_style": config.get("connector_style", ""), + "icon_to_group_mapping": config.get("icon_to_group_mapping", {}), + "custom_styles": {}, } # Merge base style with custom styles - for key, custom_style in config.get('custom_styles', {}).items(): - custom_style_dict = {item.split('=')[0]: item.split('=')[1] for item in custom_style.split(';') if item} - merged_style_dict = {**base_style_dict, **custom_style_dict} # custom style overrides base style - merged_style = ';'.join(f"{k}={v}" for k, v in merged_style_dict.items()) - styles['custom_styles'][key] = merged_style + for key, custom_style in config.get("custom_styles", {}).items(): + custom_style_dict = { + item.split("=")[0]: item.split("=")[1] + for item in custom_style.split(";") + if item + } + merged_style_dict = { + **base_style_dict, + **custom_style_dict, + } # custom style overrides base style + merged_style = ";".join(f"{k}={v}" for k, v in merged_style_dict.items()) + styles["custom_styles"][key] = merged_style # Read all other configuration values for key, value in config.items(): @@ -657,7 +797,9 @@ def load_styles_from_config(config_path): return styles -def interactive_mode(nodes, icon_to_group_mapping, containerlab_data, output_file, processor): +def interactive_mode( + nodes, icon_to_group_mapping, containerlab_data, output_file, processor +): # Initialize previous summary with existing node labels previous_summary = {"Levels": {}, "Icons": {}} for node_name, node in nodes.items(): @@ -684,7 +826,7 @@ def interactive_mode(nodes, icon_to_group_mapping, containerlab_data, output_fil title=f"Level {level} nodes", text=f"Choose the nodes for level {level}:", values=valid_nodes, - default_values=previous_summary["Levels"].get(level, []) + default_values=previous_summary["Levels"].get(level, []), ).run() else: break @@ -706,7 +848,9 @@ def interactive_mode(nodes, icon_to_group_mapping, containerlab_data, output_fil containerlab_data["topology"]["nodes"][node_name]["labels"] = {} # Update containerlab_data with graph-level - containerlab_data["topology"]["nodes"][node_name]["labels"]["graph-level"] = level + containerlab_data["topology"]["nodes"][node_name]["labels"][ + "graph-level" + ] = level tmp_nodes = list(nodes.keys()) icons = list(icon_to_group_mapping.keys()) @@ -719,7 +863,7 @@ def interactive_mode(nodes, icon_to_group_mapping, containerlab_data, output_fil title=f"Choose {icon} nodes", text=f"Select the nodes for the {icon} icon:", values=valid_nodes, - default_values=previous_summary["Icons"].get(icon, []) + default_values=previous_summary["Icons"].get(icon, []), ).run() else: icon_nodes = [] @@ -741,7 +885,9 @@ def interactive_mode(nodes, icon_to_group_mapping, containerlab_data, output_fil containerlab_data["topology"]["nodes"][node_name]["labels"] = {} # Update containerlab_data with graph-icon - containerlab_data["topology"]["nodes"][node_name]["labels"]["graph-icon"] = icon + containerlab_data["topology"]["nodes"][node_name]["labels"][ + "graph-icon" + ] = icon # Generate summary tree with combined levels and icons summary_tree = "" @@ -761,7 +907,7 @@ def interactive_mode(nodes, icon_to_group_mapping, containerlab_data, output_fil summary_tree += "\nDo you want to keep it like this? Select < No > to edit your configuration." # Prompt user for confirmation - result = yes_no_dialog(title='SUMMARY', text=summary_tree).run() + result = yes_no_dialog(title="SUMMARY", text=summary_tree).run() if result is None: return # Exit the function if cancel button is clicked @@ -769,11 +915,14 @@ def interactive_mode(nodes, icon_to_group_mapping, containerlab_data, output_fil break # Exit the loop if user confirms the summary # Prompt user if they want to update the ContainerLab file - update_file = yes_no_dialog(title='Update ContainerLab File', text="Do you want to save a new ContainerLab file with the new configuration?").run() + update_file = yes_no_dialog( + title="Update ContainerLab File", + text="Do you want to save a new ContainerLab file with the new configuration?", + ).run() if update_file: # Save the updated containerlab_data to the output file using processor.save_yaml - modified_output_file = os.path.splitext(output_file)[0] + ".mod.yaml" + modified_output_file = os.path.splitext(output_file)[0] + ".mod.yaml" processor.save_yaml(containerlab_data, modified_output_file) print(f"ContainerLab file has been updated: {modified_output_file}") else: @@ -781,6 +930,7 @@ def interactive_mode(nodes, icon_to_group_mapping, containerlab_data, output_fil return summary + def format_node_name(base_name, prefix, lab_name): if prefix == "": return base_name @@ -789,16 +939,27 @@ def format_node_name(base_name, prefix, lab_name): else: return f"{prefix}-{lab_name}-{base_name}" -def main(input_file, output_file, grafana, theme, include_unlinked_nodes=False, no_links=False, layout='vertical', verbose=False, interactive=False): + +def main( + input_file, + output_file, + grafana, + theme, + include_unlinked_nodes=False, + no_links=False, + layout="vertical", + verbose=False, + interactive=False, +): """ Generates a diagram from a given topology definition file, organizing and displaying nodes and links. - + Processes an input YAML file containing node and link definitions, extracts relevant information, and applies logic to determine node positions and connectivity. The function supports filtering out unlinked nodes, optionally excluding links, choosing the layout orientation, and toggling verbose output for detailed processing logs. """ try: - with open(input_file, 'r') as file: + with open(input_file, "r") as file: containerlab_data = yaml.safe_load(file) except FileNotFoundError: error_message = f"Error: The specified clab file '{input_file}' does not exist." @@ -809,8 +970,8 @@ def main(input_file, output_file, grafana, theme, include_unlinked_nodes=False, print(error_message) exit() - if theme in ['nokia_bright', 'nokia_dark', 'grafana_dark']: - config_path = os.path.join(script_dir, f'styles/{theme}.yaml') + if theme in ["nokia_bright", "nokia_dark", "grafana_dark"]: + config_path = os.path.join(script_dir, f"styles/{theme}.yaml") else: # Assume the user has provided a custom path config_path = theme @@ -821,39 +982,38 @@ def main(input_file, output_file, grafana, theme, include_unlinked_nodes=False, diagram = CustomDrawioDiagram() diagram.layout = layout - nodes_from_clab = containerlab_data['topology']['nodes'] + nodes_from_clab = containerlab_data["topology"]["nodes"] # Determine the prefix - prefix = containerlab_data.get('prefix', 'clab') - lab_name = containerlab_data.get('name', '') + prefix = containerlab_data.get("prefix", "clab") + lab_name = containerlab_data.get("name", "") nodes = {} for node_name, node_data in nodes_from_clab.items(): - formatted_node_name = format_node_name(node_name, prefix, lab_name) node = Node( name=formatted_node_name, label=node_name, - kind=node_data.get('kind', ''), - mgmt_ipv4=node_data.get('mgmt_ipv4', ''), - graph_level=node_data.get('labels', {}).get('graph-level', None), - graph_icon=node_data.get('labels', {}).get('graph-icon', None), - base_style=styles.get('base_style', ''), - custom_style=styles.get(node_data.get('kind', ''), ''), - pos_x=node_data.get('pos_x', ''), - pos_y=node_data.get('pos_y', ''), - width=styles.get('node_width', 75), - height=styles.get('node_height', 75), - group=node_data.get('group', '') + kind=node_data.get("kind", ""), + mgmt_ipv4=node_data.get("mgmt_ipv4", ""), + graph_level=node_data.get("labels", {}).get("graph-level", None), + graph_icon=node_data.get("labels", {}).get("graph-icon", None), + base_style=styles.get("base_style", ""), + custom_style=styles.get(node_data.get("kind", ""), ""), + pos_x=node_data.get("pos_x", ""), + pos_y=node_data.get("pos_y", ""), + width=styles.get("node_width", 75), + height=styles.get("node_height", 75), + group=node_data.get("group", ""), ) nodes[formatted_node_name] = node diagram.nodes = nodes - + # Prepare the links list by extracting source and target from each link's 'endpoints' links_from_clab = [] - for link in containerlab_data['topology'].get('links', []): - endpoints = link.get('endpoints') + for link in containerlab_data["topology"].get("links", []): + endpoints = link.get("endpoints") if endpoints: source_node, source_intf = endpoints[0].split(":") target_node, target_intf = endpoints[1].split(":") @@ -863,57 +1023,59 @@ def main(input_file, output_file, grafana, theme, include_unlinked_nodes=False, # Add link only if both source and target nodes exist if source_node in nodes and target_node in nodes: - links_from_clab.append({ - 'source': source_node, - 'target': target_node, - 'source_intf': source_intf, - 'target_intf': target_intf - }) + links_from_clab.append( + { + "source": source_node, + "target": target_node, + "source_intf": source_intf, + "target_intf": target_intf, + } + ) # Create Link instances and attach them to nodes links = [] for link_data in links_from_clab: - source_node = nodes.get(link_data['source']) - target_node = nodes.get(link_data['target']) + source_node = nodes.get(link_data["source"]) + target_node = nodes.get(link_data["target"]) if source_node and target_node: # Create two links, one for downstream and one for upstream downstream_link = Link( source=source_node, target=target_node, - source_intf=link_data.get('source_intf', ''), - target_intf=link_data.get('target_intf', ''), - base_style=styles.get('base_style', ''), - link_style=styles.get('link_style', ''), - src_label_style=styles.get('src_label_style', ''), - trgt_label_style=styles.get('trgt_label_style', ''), - entryY=link_data.get('entryY', 0), - exitY=link_data.get('exitY', 0), - entryX=link_data.get('entryX', 0), - exitX=link_data.get('exitX', 0), - direction='downstream' # Set the direction to downstream + source_intf=link_data.get("source_intf", ""), + target_intf=link_data.get("target_intf", ""), + base_style=styles.get("base_style", ""), + link_style=styles.get("link_style", ""), + src_label_style=styles.get("src_label_style", ""), + trgt_label_style=styles.get("trgt_label_style", ""), + entryY=link_data.get("entryY", 0), + exitY=link_data.get("exitY", 0), + entryX=link_data.get("entryX", 0), + exitX=link_data.get("exitX", 0), + direction="downstream", # Set the direction to downstream ) upstream_link = Link( source=target_node, target=source_node, - source_intf=link_data.get('target_intf', ''), - target_intf=link_data.get('source_intf', ''), - base_style=styles.get('base_style', ''), - link_style=styles.get('link_style', ''), - src_label_style=styles.get('src_label_style', ''), - trgt_label_style=styles.get('trgt_label_style', ''), - entryY=link_data.get('entryY', 0), - exitY=link_data.get('exitY', 0), - entryX=link_data.get('entryX', 0), - exitX=link_data.get('exitX', 0), - direction='upstream' # Set the direction to upstream + source_intf=link_data.get("target_intf", ""), + target_intf=link_data.get("source_intf", ""), + base_style=styles.get("base_style", ""), + link_style=styles.get("link_style", ""), + src_label_style=styles.get("src_label_style", ""), + trgt_label_style=styles.get("trgt_label_style", ""), + entryY=link_data.get("entryY", 0), + exitY=link_data.get("exitY", 0), + entryX=link_data.get("entryX", 0), + exitX=link_data.get("exitX", 0), + direction="upstream", # Set the direction to upstream ) links.append(downstream_link) links.append(upstream_link) - + # Add the links to the source and target nodes source_node.add_link(downstream_link) target_node.add_link(upstream_link) - + if not include_unlinked_nodes: connected_nodes = {name: node for name, node in nodes.items() if node.links} diagram.nodes = connected_nodes @@ -923,7 +1085,13 @@ def main(input_file, output_file, grafana, theme, include_unlinked_nodes=False, if interactive: processor = YAMLProcessor() - interactive_mode(diagram.nodes, styles['icon_to_group_mapping'], containerlab_data, input_file, processor) + interactive_mode( + diagram.nodes, + styles["icon_to_group_mapping"], + containerlab_data, + input_file, + processor, + ) assign_graphlevels(diagram, verbose=False) calculate_positions(diagram, layout=layout, verbose=verbose) @@ -950,10 +1118,10 @@ def main(input_file, output_file, grafana, theme, include_unlinked_nodes=False, max_size_x = max_x + 100 # Adding a margin to the right side max_size_y = max_y + 100 # Adding a margin to the bottom - if styles['pagew'] == "auto": - styles['pagew'] = max_size_x - if styles['pageh'] == "auto": - styles['pageh'] = max_size_y + if styles["pagew"] == "auto": + styles["pagew"] = max_size_x + if styles["pageh"] == "auto": + styles["pageh"] = max_size_y diagram.update_style(styles) @@ -964,15 +1132,15 @@ def main(input_file, output_file, grafana, theme, include_unlinked_nodes=False, if grafana: add_ports(diagram, styles) if not output_file: - grafana_output_file = os.path.splitext(input_file)[0] + ".grafana.json" + grafana_output_file = os.path.splitext(input_file)[0] + ".grafana.json" output_folder = os.path.dirname(grafana_output_file) or "." - output_filename = os.path.basename(grafana_output_file) + output_filename = os.path.basename(grafana_output_file) diagram.grafana_dashboard_file = grafana_output_file - os.makedirs(output_folder, exist_ok=True) + os.makedirs(output_folder, exist_ok=True) grafana = GrafanaDashboard(diagram) grafana_json = grafana.create_dashboard() # dump the json to the file - with open(grafana_output_file, 'w') as f: + with open(grafana_output_file, "w") as f: f.write(grafana_json) print("Saved file to:", grafana_output_file) else: @@ -981,7 +1149,7 @@ def main(input_file, output_file, grafana, theme, include_unlinked_nodes=False, # If output_file is not provided, generate it from input_file if not output_file: output_file = os.path.splitext(input_file)[0] + ".drawio" - + output_folder = os.path.dirname(output_file) or "." output_filename = os.path.basename(output_file) os.makedirs(output_folder, exist_ok=True) @@ -992,22 +1160,78 @@ def main(input_file, output_file, grafana, theme, include_unlinked_nodes=False, def parse_arguments(): - parser = argparse.ArgumentParser(description='Generate a topology diagram from a containerlab YAML or draw.io XML file.') - parser.add_argument('-i', '--input', required=True, help='The filename of the input file (containerlab YAML for diagram generation).') - parser.add_argument('-o', '--output', required=False, help='The output file path for the generated diagram (draw.io format).') - parser.add_argument('-g', '--gf_dashboard', action='store_true', required=False, help='Generate Grafana Dashboard Flag.') - parser.add_argument('--include-unlinked-nodes', action='store_true', help='Include nodes without any links in the topology diagram') - parser.add_argument('--no-links', action='store_true', help='Do not draw links between nodes in the topology diagram') - parser.add_argument('--layout', type=str, default='vertical', choices=['vertical', 'horizontal'], help='Specify the layout of the topology diagram (vertical or horizontal)') - parser.add_argument('--theme', default='nokia_bright', help='Specify the theme for the diagram (nokia_bright, nokia_dark, grafana_dark) or the path to a custom style config file.') - parser.add_argument('--verbose', action='store_true', help='Enable verbose output for debugging purposes') - parser.add_argument('-I', '--interactive', action='store_true', required=False, help='Define graph-levels and graph-icons in interactive mode') + parser = argparse.ArgumentParser( + description="Generate a topology diagram from a containerlab YAML or draw.io XML file." + ) + parser.add_argument( + "-i", + "--input", + required=True, + help="The filename of the input file (containerlab YAML for diagram generation).", + ) + parser.add_argument( + "-o", + "--output", + required=False, + help="The output file path for the generated diagram (draw.io format).", + ) + parser.add_argument( + "-g", + "--gf_dashboard", + action="store_true", + required=False, + help="Generate Grafana Dashboard Flag.", + ) + parser.add_argument( + "--include-unlinked-nodes", + action="store_true", + help="Include nodes without any links in the topology diagram", + ) + parser.add_argument( + "--no-links", + action="store_true", + help="Do not draw links between nodes in the topology diagram", + ) + parser.add_argument( + "--layout", + type=str, + default="vertical", + choices=["vertical", "horizontal"], + help="Specify the layout of the topology diagram (vertical or horizontal)", + ) + parser.add_argument( + "--theme", + default="nokia_bright", + help="Specify the theme for the diagram (nokia_bright, nokia_dark, grafana_dark) or the path to a custom style config file.", + ) + parser.add_argument( + "--verbose", + action="store_true", + help="Enable verbose output for debugging purposes", + ) + parser.add_argument( + "-I", + "--interactive", + action="store_true", + required=False, + help="Define graph-levels and graph-icons in interactive mode", + ) return parser.parse_args() - + + if __name__ == "__main__": args = parse_arguments() script_dir = os.path.dirname(__file__) - - main(args.input, args.output, args.gf_dashboard, args.theme, args.include_unlinked_nodes, args.no_links, args.layout, args.verbose, args.interactive) + main( + args.input, + args.output, + args.gf_dashboard, + args.theme, + args.include_unlinked_nodes, + args.no_links, + args.layout, + args.verbose, + args.interactive, + ) diff --git a/drawio2clab.py b/drawio2clab.py index b3b213b..b55ab24 100644 --- a/drawio2clab.py +++ b/drawio2clab.py @@ -1,10 +1,14 @@ -import argparse, os +import argparse +import os import xml.etree.ElementTree as ET from lib.Yaml_processor import YAMLProcessor + + def report_error(message): """Prints an error message to the console.""" print(f"Error: {message}") + def parse_xml(file_path, diagram_name=None): """ Parses an XML file and returns the mxGraphModel/root element for the specified diagram name. @@ -15,10 +19,10 @@ def parse_xml(file_path, diagram_name=None): # If a diagram name is specified, try to find the diagram by name if diagram_name: - for diagram in root.findall('diagram'): - if diagram.get('name') == diagram_name: + for diagram in root.findall("diagram"): + if diagram.get("name") == diagram_name: # Directly navigate to the mxGraphModel/root within the selected diagram - mxGraphModel_root = diagram.find('.//mxGraphModel/root') + mxGraphModel_root = diagram.find(".//mxGraphModel/root") if mxGraphModel_root is not None: return mxGraphModel_root else: @@ -28,9 +32,9 @@ def parse_xml(file_path, diagram_name=None): return None # Default to the first diagram if no name is specified - first_diagram = root.find('diagram') + first_diagram = root.find("diagram") if first_diagram is not None: - mxGraphModel_root = first_diagram.find('.//mxGraphModel/root') + mxGraphModel_root = first_diagram.find(".//mxGraphModel/root") if mxGraphModel_root is not None: return mxGraphModel_root else: @@ -40,6 +44,7 @@ def parse_xml(file_path, diagram_name=None): print("No diagrams found in the file.") return None + def extract_nodes(mxGraphModel, default_kind): """ Extracts and returns node names and their IDs from the mxGraphModel. @@ -47,49 +52,48 @@ def extract_nodes(mxGraphModel, default_kind): object elements with embedded mxCell, using the object's ID. """ node_details = {} - + # Process all objects which might contain nodes or represent nodes directly for obj in mxGraphModel.findall(".//object"): - node_id = obj.get('id') - node_label = obj.get('label', '').strip() - node_type = obj.get('type', None) # Capture the 'type' attribute - mgmt_ipv4 = obj.get('mgmt-ipv4', None) - group = obj.get('group', None) - labels = obj.get('labels', None) # Assuming 'labels' is stored directly; adjust if it's more complex - node_kind = obj.get('kind', default_kind) + node_id = obj.get("id") + node_label = obj.get("label", "").strip() + node_type = obj.get("type", None) # Capture the 'type' attribute + mgmt_ipv4 = obj.get("mgmt-ipv4", None) + group = obj.get("group", None) + labels = obj.get( + "labels", None + ) # Assuming 'labels' is stored directly; adjust if it's more complex + node_kind = obj.get("kind", default_kind) # If label is not directly on the object, try to find it in a child mxCell if not node_label: - mxCell = obj.get('mxCell') + mxCell = obj.get("mxCell") if not mxCell: continue if mxCell is not None: - node_label = mxCell.get('value', '').strip() + node_label = mxCell.get("value", "").strip() # Add to node_details if a label was found if node_label: node_details[node_id] = { - 'label': node_label, - 'type': node_type, - 'mgmt-ipv4': mgmt_ipv4, - 'group': group, - 'labels': labels, - 'kind': node_kind + "label": node_label, + "type": node_type, + "mgmt-ipv4": mgmt_ipv4, + "group": group, + "labels": labels, + "kind": node_kind, } # Process all mxCell elements that have vertex='1' for mxCell in mxGraphModel.findall(".//mxCell[@vertex='1']"): - node_id = mxCell.get('id') + node_id = mxCell.get("id") # Ensure this mxCell is not a descendant of an object element if mxCell.find("ancestor::object") is None and node_id not in node_details: - node_label = mxCell.get('value', '').strip() - style = mxCell.get('style', '') - if not "image=data" in style: + node_label = mxCell.get("value", "").strip() + style = mxCell.get("style", "") + if "image=data" not in style: continue if node_label: - node_details[node_id] = { - 'label': node_label, - 'kind': default_kind - } + node_details[node_id] = {"label": node_label, "kind": default_kind} return node_details @@ -105,59 +109,73 @@ def extract_links(mxGraphModel, node_details): for mxCell in mxGraphModel.findall(".//mxCell[@source][@target][@edge]"): link_info = extract_link_info(mxCell, node_details) if link_info: - links_info[link_info['id']] = link_info + links_info[link_info["id"]] = link_info # Process links defined within objects for object_elem in mxGraphModel.findall(".//object"): mxCells = object_elem.findall(".//mxCell[@source][@target][@edge]") for mxCell in mxCells: # Use the object's ID as a fallback if the mxCell lacks an ID - object_id = object_elem.get('id') - link_info = extract_link_info(mxCell, node_details, fallback_id=object_id) + object_id = object_elem.get("id") + link_info = extract_link_info( + mxCell, node_details, fallback_id=object_id + ) if link_info: - links_info[link_info['id']] = link_info + links_info[link_info["id"]] = link_info return links_info + def extract_link_info(mxCell, node_details, fallback_id=None): """ Extracts information for a single link from an mxCell element, - including its source, target, and geometry. + including its source, target, and geometry. """ - source_id, target_id = mxCell.get('source'), mxCell.get('target') - link_id = mxCell.get('id') or fallback_id - geometry = mxCell.find('mxGeometry') - x, y = (float(geometry.get(coord, 0)) for coord in ('x', 'y')) if geometry is not None else (None, None) + source_id, target_id = mxCell.get("source"), mxCell.get("target") + link_id = mxCell.get("id") or fallback_id + geometry = mxCell.find("mxGeometry") + x, y = ( + (float(geometry.get(coord, 0)) for coord in ("x", "y")) + if geometry is not None + else (None, None) + ) # Adjusted to access 'label' from node_details - source_label = node_details.get(source_id, {}).get('label', "Unknown") - target_label = node_details.get(target_id, {}).get('label', "Unknown") + source_label = node_details.get(source_id, {}).get("label", "Unknown") + target_label = node_details.get(target_id, {}).get("label", "Unknown") if link_id: return { - 'id': link_id, - 'source': source_label, - 'target': target_label, - 'geometry': {'x': x, 'y': y}, - 'labels': [] + "id": link_id, + "source": source_label, + "target": target_label, + "geometry": {"x": x, "y": y}, + "labels": [], } + def extract_link_labels(mxGraphModel, links_info): """ Enhances links with label information, including the label's value and geometric position. This function populates the labels array within each link's information. """ for mxCell in mxGraphModel.findall(".//mxCell[@value]"): - parent_id = mxCell.get('parent') + parent_id = mxCell.get("parent") if parent_id in links_info: - label_value, geometry = mxCell.get('value'), mxCell.find("mxGeometry") + label_value, geometry = mxCell.get("value"), mxCell.find("mxGeometry") if label_value and geometry is not None: - x_position, y_position = float(geometry.get('x', 0)), float(geometry.get('y', 0)) - links_info[parent_id]['labels'].append({ - 'value': label_value, - 'x_position': x_position, - 'y_position': y_position - }) + x_position, y_position = ( + float(geometry.get("x", 0)), + float(geometry.get("y", 0)), + ) + links_info[parent_id]["labels"].append( + { + "value": label_value, + "x_position": x_position, + "y_position": y_position, + } + ) + def aggregate_node_information(node_details): """ @@ -169,43 +187,57 @@ def aggregate_node_information(node_details): for node_id, details in node_details.items(): # Use the existing 'kind' if it's explicitly defined and not default 'nokia_srlinux', or # apply custom logic to determine 'kind'. - if details.get('kind') and details.get('kind') != 'nokia_srlinux': + if details.get("kind") and details.get("kind") != "nokia_srlinux": # 'kind' is explicitly provided, so we keep it. - node_kind = details['kind'] + node_kind = details["kind"] else: # Apply custom logic: if 'client' in label, set as 'linux'; otherwise, keep existing or default to 'nokia_srlinux'. - node_kind = 'linux' if 'client' in details['label'] else details.get('kind', 'nokia_srlinux') - + node_kind = ( + "linux" + if "client" in details["label"] + else details.get("kind", "nokia_srlinux") + ) + # Update node details with potentially modified 'kind'. updated_details = details.copy() - updated_details['kind'] = node_kind + updated_details["kind"] = node_kind updated_node_details[node_id] = updated_details return updated_node_details -def compile_link_information(links_info, style='block'): + +def compile_link_information(links_info, style="block"): """ - Compiles and formats link information into a structured format. + Compiles and formats link information into a structured format. When there are three or more labels on a link, only the labels closest to the source and destination are considered. The 'style' parameter determines the format of the endpoints in the output. """ compiled_links = [] for link_id, info in links_info.items(): - sorted_labels = sorted(info['labels'], key=lambda label: label['x_position']) - + sorted_labels = sorted(info["labels"], key=lambda label: label["x_position"]) + # Handle insufficient labels gracefully if len(sorted_labels) < 2: - report_error(f"Not enough labels for link {link_id}. At least 2 labels are required.") + report_error( + f"Not enough labels for link {link_id}. At least 2 labels are required." + ) continue # Skip this link - source_label = sorted_labels[0]['value'] - target_label = sorted_labels[-1]['value'] - - endpoints = [f"{info['source']}:{source_label}", f"{info['target']}:{target_label}"] - compiled_links.append({'endpoints': endpoints}) + source_label = sorted_labels[0]["value"] + target_label = sorted_labels[-1]["value"] + + endpoints = [ + f"{info['source']}:{source_label}", + f"{info['target']}:{target_label}", + ] + compiled_links.append({"endpoints": endpoints}) # Sort the compiled_links list by the source of the endpoint - compiled_links.sort(key=lambda x: (x['endpoints'][0].split(':')[0] if style == 'block' else x['endpoints'])) + compiled_links.sort( + key=lambda x: ( + x["endpoints"][0].split(":")[0] if style == "block" else x["endpoints"] + ) + ) return compiled_links @@ -216,13 +248,20 @@ def filter_nodes(links_info, node_details): This helps to eliminate any nodes that are not part of the actual topology being described. """ # Collect labels of nodes involved in links - involved_labels = {info['source'] for info in links_info.values()} | {info['target'] for info in links_info.values()} + involved_labels = {info["source"] for info in links_info.values()} | { + info["target"] for info in links_info.values() + } # Filter node_details to include only those nodes with labels involved in links - filtered_details = {node_id: details for node_id, details in node_details.items() if details['label'] in involved_labels} + filtered_details = { + node_id: details + for node_id, details in node_details.items() + if details["label"] in involved_labels + } return filtered_details + def generate_yaml_structure(filtered_node_details, compiled_links, input_file): """ Generates the final YAML structure based on filtered node details and compiled link information, @@ -234,27 +273,29 @@ def generate_yaml_structure(filtered_node_details, compiled_links, input_file): kinds = {} for node_id, details in filtered_node_details.items(): - node_label = details['label'] - node_kind = details['kind'] + node_label = details["label"] + node_kind = details["kind"] # Dynamically construct the kinds dictionary with default settings for known kinds if node_kind not in kinds: - if node_kind == 'nokia_srlinux': - kinds[node_kind] = {'image': 'ghcr.io/nokia/srlinux', 'type': 'ixrd3'} - elif node_kind == 'linux': - kinds[node_kind] = {'image': 'ghcr.io/hellt/network-multitool'} - elif node_kind == 'vr-sros': - kinds[node_kind] = {'image': 'registry.srlinux.dev/pub/vr-sros:23.10'} + if node_kind == "nokia_srlinux": + kinds[node_kind] = {"image": "ghcr.io/nokia/srlinux", "type": "ixrd3"} + elif node_kind == "linux": + kinds[node_kind] = {"image": "ghcr.io/hellt/network-multitool"} + elif node_kind == "vr-sros": + kinds[node_kind] = {"image": "registry.srlinux.dev/pub/vr-sros:23.10"} else: - kinds[node_kind] = {'image': None} # Add more conditions as necessary for other kinds + kinds[node_kind] = { + "image": None + } # Add more conditions as necessary for other kinds # Prepare node information, conditionally including 'type' if it exists and is not None - node_info = {'kind': node_kind} - if details.get('type'): - node_info['type'] = details['type'] + node_info = {"kind": node_kind} + if details.get("type"): + node_info["type"] = details["type"] # Optionally add other details if they exist and are not None - for attr in ['mgmt-ipv4', 'group', 'labels']: + for attr in ["mgmt-ipv4", "group", "labels"]: attr_value = details.get(attr) if attr_value is not None: # Check for None to exclude 'null' values node_info[attr] = attr_value @@ -263,15 +304,18 @@ def generate_yaml_structure(filtered_node_details, compiled_links, input_file): nodes[node_label] = node_info return { - 'name': base_name, - 'topology': { - 'kinds': kinds, - 'nodes': nodes, - 'links': compiled_links - } + "name": base_name, + "topology": {"kinds": kinds, "nodes": nodes, "links": compiled_links}, } - -def main(input_file, output_file, style='block', diagram_name=None, default_kind='nokia_srlinux'): + + +def main( + input_file, + output_file, + style="block", + diagram_name=None, + default_kind="nokia_srlinux", +): """ The main function orchestrates the parsing, extraction, and processing of .drawio XML content, and then generates and writes the YAML file. It ties together all the steps necessary to convert @@ -295,15 +339,52 @@ def main(input_file, output_file, style='block', diagram_name=None, default_kind if style == "flow": processor.save_yaml(yaml_data, output_file) else: - processor.save_yaml(yaml_data, output_file, flow_style='block') + processor.save_yaml(yaml_data, output_file, flow_style="block") + if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Parse a draw.io XML file and generate a YAML file with a specified style.") - parser.add_argument("-i", "--input", dest="input_file", required=True, help="The input XML file to be parsed.") - parser.add_argument("-o", "--output", dest="output_file", required=False, help="The output YAML file.") - parser.add_argument("--style", dest="style", choices=['block', 'flow'], default="flow", help="The style for YAML endpoints. Choose 'block' or 'flow'. Default is 'block'.") - parser.add_argument("--diagram-name", dest="diagram_name", required=False, help="The name of the diagram (tab) to be parsed.") - parser.add_argument("--default-kind", dest="default_kind", default="nokia_srlinux", help="The default kind for nodes. Default is 'nokia_srlinux'.") + parser = argparse.ArgumentParser( + description="Parse a draw.io XML file and generate a YAML file with a specified style." + ) + parser.add_argument( + "-i", + "--input", + dest="input_file", + required=True, + help="The input XML file to be parsed.", + ) + parser.add_argument( + "-o", + "--output", + dest="output_file", + required=False, + help="The output YAML file.", + ) + parser.add_argument( + "--style", + dest="style", + choices=["block", "flow"], + default="flow", + help="The style for YAML endpoints. Choose 'block' or 'flow'. Default is 'block'.", + ) + parser.add_argument( + "--diagram-name", + dest="diagram_name", + required=False, + help="The name of the diagram (tab) to be parsed.", + ) + parser.add_argument( + "--default-kind", + dest="default_kind", + default="nokia_srlinux", + help="The default kind for nodes. Default is 'nokia_srlinux'.", + ) args = parser.parse_args() - main(args.input_file, args.output_file, args.style, args.diagram_name, args.default_kind) \ No newline at end of file + main( + args.input_file, + args.output_file, + args.style, + args.diagram_name, + args.default_kind, + ) diff --git a/lib/CustomDrawioDiagram.py b/lib/CustomDrawioDiagram.py index 100df3e..c43f7ff 100644 --- a/lib/CustomDrawioDiagram.py +++ b/lib/CustomDrawioDiagram.py @@ -1,5 +1,7 @@ from N2G import drawio_diagram import xml.etree.ElementTree as ET + + class CustomDrawioDiagram(drawio_diagram): # Overriding the drawio_diagram_xml with shadow=0 drawio_diagram_xml = """ @@ -12,15 +14,14 @@ class CustomDrawioDiagram(drawio_diagram): """ - - def __init__(self, styles=None, node_duplicates="skip", link_duplicates="skip"): + def __init__(self, styles=None, node_duplicates="skip", link_duplicates="skip"): if styles: - background = styles['background'] - shadow = styles['shadow'] - grid = styles['grid'] - pagew = styles['pagew'] - pageh = styles['pageh'] + background = styles["background"] + shadow = styles["shadow"] + grid = styles["grid"] + pagew = styles["pagew"] + pageh = styles["pageh"] self.drawio_diagram_xml = f""" @@ -33,19 +34,22 @@ def __init__(self, styles=None, node_duplicates="skip", link_duplicates="skip"): """ - super().__init__(node_duplicates, link_duplicates, ) + super().__init__( + node_duplicates, + link_duplicates, + ) def calculate_new_group_positions(self, obj_pos_old, group_pos): # Adjust object positions relative to the new group's position obj_pos_new = (obj_pos_old[0] - group_pos[0], obj_pos_old[1] - group_pos[1]) return obj_pos_new - + def update_style(self, styles): - background = styles['background'] - shadow = styles['shadow'] - grid = styles['grid'] - pagew = styles['pagew'] - pageh = styles['pageh'] + background = styles["background"] + shadow = styles["shadow"] + grid = styles["grid"] + pagew = styles["pagew"] + pageh = styles["pageh"] self.drawio_diagram_xml = f""" @@ -61,8 +65,8 @@ def update_style(self, styles): def group_nodes(self, member_objects, group_id, style=""): # Initialize bounding box coordinates - min_x = min_y = float('inf') - max_x = max_y = float('-inf') + min_x = min_y = float("inf") + max_x = max_y = float("-inf") object_positions = [] # To store all object positions @@ -72,8 +76,11 @@ def group_nodes(self, member_objects, group_id, style=""): if obj_mxcell is not None: geometry = obj_mxcell.find("./mxGeometry") if geometry is not None: - x, y = float(geometry.get('x', '0')), float(geometry.get('y', '0')) - width, height = float(geometry.get('width', '0')), float(geometry.get('height', '0')) + x, y = float(geometry.get("x", "0")), float(geometry.get("y", "0")) + width, height = ( + float(geometry.get("width", "0")), + float(geometry.get("height", "0")), + ) # Store object positions and update bounding box object_positions.append((obj_id, x, y, width, height)) @@ -96,43 +103,47 @@ def group_nodes(self, member_objects, group_id, style=""): # Update positions of all objects within the group for obj_id, x, y, _, _ in object_positions: obj_pos_old = (x, y) - obj_pos_new = self.calculate_new_group_positions(obj_pos_old, (group_x, group_y)) + obj_pos_new = self.calculate_new_group_positions( + obj_pos_old, (group_x, group_y) + ) obj_mxcell = self.current_root.find(f".//object[@id='{obj_id}']/mxCell") if obj_mxcell is not None: geometry = obj_mxcell.find("./mxGeometry") if geometry is not None: - geometry.set('x', str(obj_pos_new[0])) - geometry.set('y', str(obj_pos_new[1])) - obj_mxcell.set("parent", group_id) # Set the object's parent to the new group + geometry.set("x", str(obj_pos_new[0])) + geometry.set("y", str(obj_pos_new[1])) + obj_mxcell.set( + "parent", group_id + ) # Set the object's parent to the new group def get_used_levels(self): return set([node.graph_level for node in self.nodes.values()]) def get_max_level(self): return max([node.graph_level for node in self.nodes.values()]) - + def get_min_level(self): return min([node.graph_level for node in self.nodes.values()]) - + def get_links_from_nodes(self): links = [] for node in self.nodes.values(): links.extend(node.get_all_links()) return links - + def get_upstream_links_from_nodes(self): links = [] for node in self.nodes.values(): links.extend(node.get_upstream_links()) return links - + def get_downstream_links_from_nodes(self): links = [] for node in self.nodes.values(): links.extend(node.get_downstream_links()) return links - + def get_lateral_links_from_nodes(self): links = [] for node in self.nodes.values(): @@ -141,19 +152,21 @@ def get_lateral_links_from_nodes(self): def get_target_link(self, source_link): for link in self.get_links_from_nodes(): - if link.source == source_link.target \ - and link.target == source_link.source \ - and (link.direction != 'lateral' or link.direction == source_link.direction) \ - and source_link.source_intf == link.target_intf \ - and source_link.target_intf == link.source_intf: + if ( + link.source == source_link.target + and link.target == source_link.source + and ( + link.direction != "lateral" + or link.direction == source_link.direction + ) + and source_link.source_intf == link.target_intf + and source_link.target_intf == link.source_intf + ): return link return None - - def get_nodes(self): return self.nodes - def get_nodes_with_same_xy(self): nodes_with_same_x = {} @@ -190,10 +203,16 @@ def get_nodes_between_interconnected(self): # Check if there are any nodes between node1 and node2 based on their positions for node_between in nodes: if node_between != node1 and node_between != node2: - if (node1.pos_y < node_between.pos_y < node2.pos_y) or (node2.pos_y < node_between.pos_y < node1.pos_y): - if node_between not in nodes_between_interconnected_x: - nodes_between_interconnected_x.append(node_between) - + if (node1.pos_y < node_between.pos_y < node2.pos_y) or ( + node2.pos_y < node_between.pos_y < node1.pos_y + ): + if ( + node_between + not in nodes_between_interconnected_x + ): + nodes_between_interconnected_x.append( + node_between + ) for coord, nodes in nodes_with_same_y.items(): for i in range(len(nodes)): @@ -204,13 +223,19 @@ def get_nodes_between_interconnected(self): # Check if there are any nodes between node1 and node2 based on their positions for node_between in nodes: if node_between != node1 and node_between != node2: - if (node1.pos_x < node_between.pos_x < node2.pos_x) or (node2.pos_x < node_between.pos_x < node1.pos_x): - if node_between not in nodes_between_interconnected_y: - nodes_between_interconnected_y.append(node_between) + if (node1.pos_x < node_between.pos_x < node2.pos_x) or ( + node2.pos_x < node_between.pos_x < node1.pos_x + ): + if ( + node_between + not in nodes_between_interconnected_y + ): + nodes_between_interconnected_y.append( + node_between + ) return nodes_between_interconnected_x, nodes_between_interconnected_y - def get_nodes_by_level(self, level): nodes_by_level = {} for node in self.nodes.values(): diff --git a/lib/Grafana.py b/lib/Grafana.py index 4dd46ac..2bcc90d 100644 --- a/lib/Grafana.py +++ b/lib/Grafana.py @@ -2,40 +2,40 @@ import os import xml.etree.ElementTree as ET + class GrafanaDashboard: def __init__(self, diagram=None): self.diagram = diagram self.links = self.diagram.get_links_from_nodes() self.dashboard_filename = self.diagram.grafana_dashboard_file - def create_dashboard(self): # We just need the subtree objects from mxGraphModel.Single page drawings only xmlTree = ET.fromstring(self.diagram.dump_xml()) - subXmlTree = xmlTree.findall('.//mxGraphModel')[0] + subXmlTree = xmlTree.findall(".//mxGraphModel")[0] # Define Query rules for the Panel, rule_expr needs to match the collector metric name # Legend format needs to match the format expected by the metric panelQueryList = { "IngressTraffic": { "rule_expr": "interface_traffic_rate_in_bps", - "legend_format": '{{source}}:{{interface_name}}:in', + "legend_format": "{{source}}:{{interface_name}}:in", }, "EgressTraffic": { "rule_expr": "interface_traffic_rate_out_bps", - "legend_format": '{{source}}:{{interface_name}}:out', + "legend_format": "{{source}}:{{interface_name}}:out", }, "ItfOperState": { "rule_expr": "interface_oper_state", - "legend_format": 'oper_state:{{source}}:{{interface_name}}', + "legend_format": "oper_state:{{source}}:{{interface_name}}", }, "ItfOperState2": { "rule_expr": "port_oper_state", - "legend_format": 'oper_state:{{source}}:{{interface_name}}', + "legend_format": "oper_state:{{source}}:{{interface_name}}", }, "EgressTraffic2": { "rule_expr": "irate(port_ethernet_statistics_out_octets[$__rate_interval])*8", - "legend_format": '{{source}}:{{interface_name}}:out', + "legend_format": "{{source}}:{{interface_name}}:out", }, } # Create a targets list to embed in the JSON object, we add all the other default JSON attributes to the list @@ -53,7 +53,7 @@ def create_dashboard(self): rulesData = [] i = 0 for link in self.links: - link_id = f"link_id:{link.source.name}:{link.source_intf}:{link.target.name}:{link.target_intf}" + link_id = f"link_id:{link.source.name}:{link.source_intf}:{link.target.name}:{link.target_intf}" # Traffic out rulesData.append( @@ -64,10 +64,10 @@ def create_dashboard(self): order=i, ) ) - + i = i + 2 - - port_id = f"{link.source.name}:{link.source_intf}:{link.target.name}:{link.target_intf}" + + port_id = f"{link.source.name}:{link.source_intf}:{link.target.name}:{link.target_intf}" # Port State: rulesData.append( self.gf_flowchart_rule_operstate( @@ -96,15 +96,14 @@ def create_dashboard(self): ) return dashboard_json - def gf_dashboard_datasource_target(self, rule_expr="promql_query", legend_format=None, refId="Query1"): + def gf_dashboard_datasource_target( + self, rule_expr="promql_query", legend_format=None, refId="Query1" + ): """ Dictionary containing information relevant to the Targets queried """ target = { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, + "datasource": {"type": "prometheus", "uid": "${DS_PROMETHEUS}"}, "editorMode": "code", "expr": rule_expr, "instant": False, @@ -114,14 +113,18 @@ def gf_dashboard_datasource_target(self, rule_expr="promql_query", legend_format } return target - def gf_flowchart_rule_traffic(self, ruleName="traffic:inOrOut", metric=None, link_id=None, order=1): + def gf_flowchart_rule_traffic( + self, ruleName="traffic:inOrOut", metric=None, link_id=None, order=1 + ): """ Dictionary containing information relevant to the traffic Rules """ # Load the traffic rule template from file - base_dir = os.getenv('APP_BASE_DIR', '') + base_dir = os.getenv("APP_BASE_DIR", "") - with open(os.path.join(base_dir, "lib/templates/traffic_rule_template.json"), "r") as f: + with open( + os.path.join(base_dir, "lib/templates/traffic_rule_template.json"), "r" + ) as f: rule = json.load(f) rule["alias"] = ruleName @@ -132,14 +135,18 @@ def gf_flowchart_rule_traffic(self, ruleName="traffic:inOrOut", metric=None, lin return rule - def gf_flowchart_rule_operstate(self, ruleName="oper_state", metric=None, link_id=None, order=1): + def gf_flowchart_rule_operstate( + self, ruleName="oper_state", metric=None, link_id=None, order=1 + ): """ Dictionary containing information relevant to the Operational State Rules """ # Load the operstate rule template from file - base_dir = os.getenv('APP_BASE_DIR', '') + base_dir = os.getenv("APP_BASE_DIR", "") - with open(os.path.join(base_dir,"lib/templates/operstate_rule_template.json"), "r") as f: + with open( + os.path.join(base_dir, "lib/templates/operstate_rule_template.json"), "r" + ) as f: rule = json.load(f) rule["alias"] = ruleName @@ -149,15 +156,19 @@ def gf_flowchart_rule_operstate(self, ruleName="oper_state", metric=None, link_i return rule - def gf_flowchart_panel_template(self, xml=None, rulesData=None, targetsList=None, panelTitle="Network Topology"): + def gf_flowchart_panel_template( + self, xml=None, rulesData=None, targetsList=None, panelTitle="Network Topology" + ): """ Dictionary containing information relevant to the Panels Section in the JSON Dashboard Embedding of the XML diagram, the Rules and the Targets """ # Load the panel template from file - base_dir = os.getenv('APP_BASE_DIR', '') + base_dir = os.getenv("APP_BASE_DIR", "") - with open(os.path.join(base_dir,"lib/templates/panel_template.json"), "r") as f: + with open( + os.path.join(base_dir, "lib/templates/panel_template.json"), "r" + ) as f: panel = json.load(f) panel[0]["flowchartsData"]["flowcharts"][0]["xml"] = xml @@ -172,13 +183,15 @@ def gf_dashboard_template(self, panels=None, dashboard_name="lab-telemetry"): Dictionary containing information relevant to the Grafana Dashboard Root JSON object """ - base_dir = os.getenv('APP_BASE_DIR', '') + base_dir = os.getenv("APP_BASE_DIR", "") # Load the dashboard template from file - with open(os.path.join(base_dir, "lib/templates/traffic_rule_template.json")) as f: + with open( + os.path.join(base_dir, "lib/templates/traffic_rule_template.json") + ) as f: dashboard = json.load(f) dashboard["panels"] = panels dashboard["title"] = dashboard_name - return dashboard \ No newline at end of file + return dashboard diff --git a/lib/Link.py b/lib/Link.py index 5fa0dc4..32087f8 100644 --- a/lib/Link.py +++ b/lib/Link.py @@ -4,34 +4,34 @@ def __init__(self, source, target, source_intf=None, target_intf=None, **kwargs) self.target = target self.source_intf = source_intf self.target_intf = target_intf - self.direction = kwargs.get('direction', '') - self.theme = kwargs.get('theme', 'nokia_bright') - self.base_style = kwargs.get('base_style', '') - self.link_style = kwargs.get('link_style', '') - self.src_label_style = kwargs.get('src_label_style', '') - self.trgt_label_style = kwargs.get('trgt_label_style', '') - self.entryY = kwargs.get('entryY', 0) - self.exitY = kwargs.get('exitY', 0) - self.entryX = kwargs.get('entryX', 0) - self.exitX = kwargs.get('exitX', 0) + self.direction = kwargs.get("direction", "") + self.theme = kwargs.get("theme", "nokia_bright") + self.base_style = kwargs.get("base_style", "") + self.link_style = kwargs.get("link_style", "") + self.src_label_style = kwargs.get("src_label_style", "") + self.trgt_label_style = kwargs.get("trgt_label_style", "") + self.entryY = kwargs.get("entryY", 0) + self.exitY = kwargs.get("exitY", 0) + self.entryX = kwargs.get("entryX", 0) + self.exitX = kwargs.get("exitX", 0) def set_styles(self, **kwargs): - self.base_style = kwargs.get('base_style', self.base_style) - self.link_style = kwargs.get('link_style', self.link_style) - self.src_label_style = kwargs.get('src_label_style', self.src_label_style) - self.trgt_label_style = kwargs.get('trgt_label_style', self.trgt_label_style) + self.base_style = kwargs.get("base_style", self.base_style) + self.link_style = kwargs.get("link_style", self.link_style) + self.src_label_style = kwargs.get("src_label_style", self.src_label_style) + self.trgt_label_style = kwargs.get("trgt_label_style", self.trgt_label_style) def set_entry_exit_points(self, **kwargs): - self.entryY = kwargs.get('entryY', self.entryY) - self.exitY = kwargs.get('exitY', self.exitY) - self.entryX = kwargs.get('entryX', self.entryX) - self.exitX = kwargs.get('exitX', self.exitX) + self.entryY = kwargs.get("entryY", self.entryY) + self.exitY = kwargs.get("exitY", self.exitY) + self.entryX = kwargs.get("entryX", self.entryX) + self.exitX = kwargs.get("exitX", self.exitX) def generate_style_string(self): style = f"{self.base_style}{self.link_style}" style += f"entryY={self.entryY};exitY={self.exitY};" style += f"entryX={self.entryX};exitX={self.exitX};" return style - + def __repr__(self): - return f"Link(source='{self.source}', target='{self.target}')" \ No newline at end of file + return f"Link(source='{self.source}', target='{self.target}')" diff --git a/lib/Node.py b/lib/Node.py index c600843..8c34f69 100644 --- a/lib/Node.py +++ b/lib/Node.py @@ -1,5 +1,14 @@ class Node: - def __init__(self, name, label, kind, mgmt_ipv4=None, graph_level=None, graph_icon=None, **kwargs): + def __init__( + self, + name, + label, + kind, + mgmt_ipv4=None, + graph_level=None, + graph_icon=None, + **kwargs, + ): self.name = name self.label = label self.kind = kind @@ -9,13 +18,13 @@ def __init__(self, name, label, kind, mgmt_ipv4=None, graph_level=None, graph_ic self.links = [] self.categories = [] self.properties = kwargs - self.base_style = kwargs.get('base_style', '') - self.custom_style = kwargs.get('custom_style', '') - self.pos_x = kwargs.get('pos_x', '') - self.pos_y = kwargs.get('pos_y', '') - self.width = kwargs.get('width', '') - self.height = kwargs.get('height', '') - self.group = kwargs.get('group', '') + self.base_style = kwargs.get("base_style", "") + self.custom_style = kwargs.get("custom_style", "") + self.pos_x = kwargs.get("pos_x", "") + self.pos_y = kwargs.get("pos_y", "") + self.width = kwargs.get("width", "") + self.height = kwargs.get("height", "") + self.group = kwargs.get("group", "") def add_link(self, link): self.links.append(link) @@ -25,22 +34,33 @@ def remove_link(self, link): def get_connection_count(self): return len(self.links) - + def get_connection_count_within_level(self): - return len([link for link in self.links if link.source.graph_level == self.graph_level or link.target.graph_level == self.graph_level]) - + return len( + [ + link + for link in self.links + if link.source.graph_level == self.graph_level + or link.target.graph_level == self.graph_level + ] + ) + def get_downstream_links(self): - return [link for link in self.links if link.direction == 'downstream'] - + return [link for link in self.links if link.direction == "downstream"] + def get_upstream_links(self): - return [link for link in self.links if link.direction == 'upstream'] - + return [link for link in self.links if link.direction == "upstream"] + def get_upstream_links_towards_level(self, level): - return [link for link in self.links if link.direction == 'upstream' and link.target.graph_level == level] + return [ + link + for link in self.links + if link.direction == "upstream" and link.target.graph_level == level + ] def get_lateral_links(self): - return [link for link in self.links if link.direction == 'lateral'] - + return [link for link in self.links if link.direction == "lateral"] + def get_all_links(self): return self.links @@ -52,7 +72,7 @@ def get_neighbors(self): else: neighbors.add(link.source) return list(neighbors) - + def is_connected_to(self, node): for link in self.links: if link.source == node or link.target == node: @@ -77,11 +97,11 @@ def update_links(self): target_level = link.target.graph_level link.level_diff = target_level - source_level if link.level_diff > 0: - link.direction = 'downstream' + link.direction = "downstream" elif link.level_diff < 0: - link.direction = 'upstream' + link.direction = "upstream" else: - link.direction = 'lateral' + link.direction = "lateral" def __repr__(self): - return f"Node(name='{self.name}', kind='{self.kind}')" \ No newline at end of file + return f"Node(name='{self.name}', kind='{self.kind}')" diff --git a/lib/Yaml_processor.py b/lib/Yaml_processor.py index 3f40c4c..2aa3b85 100644 --- a/lib/Yaml_processor.py +++ b/lib/Yaml_processor.py @@ -1,19 +1,25 @@ import yaml import sys + class YAMLProcessor: class CustomDumper(yaml.SafeDumper): """ Custom YAML dumper that adjusts the indentation for lists and maintains certain lists in inline format. """ + pass def custom_list_representer(self, dumper, data): # Check if we are at the specific list under 'links' with 'endpoints' - if len(data) == 2 and isinstance(data[0], str) and ':' in data[0]: - return dumper.represent_sequence(u'tag:yaml.org,2002:seq', data, flow_style=True) + if len(data) == 2 and isinstance(data[0], str) and ":" in data[0]: + return dumper.represent_sequence( + "tag:yaml.org,2002:seq", data, flow_style=True + ) else: - return dumper.represent_sequence(u'tag:yaml.org,2002:seq', data, flow_style=False) + return dumper.represent_sequence( + "tag:yaml.org,2002:seq", data, flow_style=False + ) def custom_dict_representer(self, dumper, data): return dumper.represent_dict(data.items()) @@ -36,9 +42,16 @@ def load_yaml(self, yaml_str): def save_yaml(self, data, output_file, flow_style=None): try: # Save YAML data - with open(output_file, 'w') as file: + with open(output_file, "w") as file: if flow_style is None: - yaml.dump(data, file, Dumper=self.CustomDumper, sort_keys=False, default_flow_style=False, indent=2) + yaml.dump( + data, + file, + Dumper=self.CustomDumper, + sort_keys=False, + default_flow_style=False, + indent=2, + ) else: yaml.dump(data, file, default_flow_style=False, sort_keys=False) @@ -46,4 +59,4 @@ def save_yaml(self, data, output_file, flow_style=None): except IOError as e: print(f"Error saving YAML file: {str(e)}") - sys.exit(1) \ No newline at end of file + sys.exit(1) From ef36279ca020a35521ccea4172495747df0ee4f3 Mon Sep 17 00:00:00 2001 From: FloSch62 Date: Wed, 12 Jun 2024 11:54:46 +0200 Subject: [PATCH 3/5] removed leftovers --- clab2drawio.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/clab2drawio.py b/clab2drawio.py index 49f5bce..0563ef2 100644 --- a/clab2drawio.py +++ b/clab2drawio.py @@ -479,7 +479,6 @@ def calculate_positions(diagram, layout="vertical", verbose=False): x_start, y_start = 100, 100 padding_x, padding_y = 150, 175 - min_margin = 150 if verbose: print("Nodes before calculate_positions:", nodes) @@ -594,13 +593,8 @@ def adjust_node_levels(diagram): continue nodes_at_current_level = diagram.get_nodes_by_level(current_level) - nodes_at_next_level = diagram.get_nodes_by_level(current_level + 1) - # print(f"Processing level {current_level}:") - # print(f"Nodes at current level: {{current_level}} {[node.name for node in nodes_at_current_level.values()]}") - next_level = current_level + 1 before_level = current_level - 1 nodes_to_move = [] - # if nodes_at_next_level: if len(nodes_at_current_level.items()) == 1: # print(f"Only one node found at level {current_level}. No adjustment needed.") @@ -613,16 +607,11 @@ def adjust_node_levels(diagram): if not has_upstream_connection: nodes_to_move.append(node) - # else: - # print(f"Node {node_name} has {len(node.get_upstream_links_towards_level(before_level))} upstream links against Level {before_level} No adjustment needed.") if len(nodes_to_move) == len(nodes_at_current_level): # print(f"Nothing to move here") current_level += 1 continue - # else: - # for node in nodes_to_move: - # print(f"!Node {node.name} does not have an upstream connection to level {before_level}. Marked for movement.") if nodes_to_move: # print(f"Because we need to move, we are increasing all node_graphlevels from the next Levels Nodes by one level") From e2031891cda8de8642b1dd26c3b4c667ee450385 Mon Sep 17 00:00:00 2001 From: FloSch62 Date: Wed, 12 Jun 2024 13:14:34 +0200 Subject: [PATCH 4/5] lower case for metrics --- lib/Grafana.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Grafana.py b/lib/Grafana.py index 2bcc90d..7f82754 100644 --- a/lib/Grafana.py +++ b/lib/Grafana.py @@ -59,7 +59,7 @@ def create_dashboard(self): rulesData.append( self.gf_flowchart_rule_traffic( ruleName=f"{link.source.name}:{link.source_intf}:out", - metric=f"{link.source.name}:{link.source_intf}:out", + metric=f"{link.source.name.lower()}:{link.source_intf}:out", link_id=link_id, order=i, ) @@ -72,7 +72,7 @@ def create_dashboard(self): rulesData.append( self.gf_flowchart_rule_operstate( ruleName=f"oper_state:{link.source.name}:{link.source_intf}", - metric=f"oper_state:{link.source.name}:{link.source_intf}", + metric=f"oper_state:{link.source.name.lower()}:{link.source_intf}", link_id=port_id, order=i + 3, ) From dee97213d358d4a52bca65f6d5b0481774a09a32 Mon Sep 17 00:00:00 2001 From: FloSch62 Date: Wed, 12 Jun 2024 13:29:32 +0200 Subject: [PATCH 5/5] updated themes --- styles/grafana_dark.yaml | 4 ++-- styles/nokia_bright.yaml | 4 ++-- styles/nokia_dark.yaml | 9 +++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/styles/grafana_dark.yaml b/styles/grafana_dark.yaml index 2b7128e..e5abb8f 100644 --- a/styles/grafana_dark.yaml +++ b/styles/grafana_dark.yaml @@ -30,8 +30,8 @@ custom_styles: default: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" spine: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" leaf: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" - dcgw: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIiB4PSIwIi8+JiN4YTs8Zz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTQ5LjcsNzBMMjAuMSw5OS44IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNOTcuNyw5Ny40TDY4LDY3LjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8L2c+JiN4YTsJPGc+JiN4YTsJCTxwYXRoIGQ9Ik03MC40LDQ5LjdMOTkuOSwyMCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8cGF0aCBkPSJNMjIuMywyMi4zTDUyLDUxLjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8cGF0aCBkPSJNMjAuMSwzMy45bDAtMTAuN2MwLTEuOCwxLjMtMywzLjEtMy4xbDEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik0zOC40LDY4bDEwLjcsMGMxLjgsMCwzLDEuMywzLjEsMy4xbDAsMTAuOCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik05OS44LDg2LjJsMCwxMC43YzAsMS44LTEuMywzLTMuMSwzLjFsLTEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik04MS44LDUxLjlsLTEwLjcsMGMtMS44LDAtMy0xLjMtMy4xLTMuMUw2OCwzOCIgY2xhc3M9InN0MSIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#ffffff;labelPosition=left;align=right;" - server: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIi8+JiN4YTs8Zz4mI3hhOwk8cGF0aCBkPSJNMTAwLDkxLjFIMjBIMTAweiBNODkuMSwzMi41YzAtMC41LDAtMS0wLjItMS40Yy0wLjItMC41LTAuNC0wLjktMC44LTEuMmMtMC4zLTAuMy0wLjgtMC42LTEuMi0wLjgmIzEwOyYjOTsmIzk7Yy0wLjUtMC4yLTAuOS0wLjItMS40LTAuMkgzNC42Yy0wLjUsMC0xLDAtMS40LDAuMmMtMC41LDAuMi0wLjksMC40LTEuMiwwLjhjLTAuMywwLjMtMC42LDAuOC0wLjgsMS4yYy0wLjIsMC41LTAuMiwwLjktMC4yLDEuNCYjMTA7JiM5OyYjOTtWNzZoNTguMlYzMi41eiIgY2xhc3M9InN0NCIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#ffffff;labelPosition=left;align=right;" + dcgw: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIiB4PSIwIi8+JiN4YTs8Zz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTQ5LjcsNzBMMjAuMSw5OS44IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNOTcuNyw5Ny40TDY4LDY3LjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8L2c+JiN4YTsJPGc+JiN4YTsJCTxwYXRoIGQ9Ik03MC40LDQ5LjdMOTkuOSwyMCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8cGF0aCBkPSJNMjIuMywyMi4zTDUyLDUxLjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8cGF0aCBkPSJNMjAuMSwzMy45bDAtMTAuN2MwLTEuOCwxLjMtMywzLjEtMy4xbDEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik0zOC40LDY4bDEwLjcsMGMxLjgsMCwzLDEuMywzLjEsMy4xbDAsMTAuOCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik05OS44LDg2LjJsMCwxMC43YzAsMS44LTEuMywzLTMuMSwzLjFsLTEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik04MS44LDUxLjlsLTEwLjcsMGMtMS44LDAtMy0xLjMtMy4xLTMuMUw2OCwzOCIgY2xhc3M9InN0MSIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" + server: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIi8+JiN4YTs8Zz4mI3hhOwk8cGF0aCBkPSJNMTAwLDkxLjFIMjBIMTAweiBNODkuMSwzMi41YzAtMC41LDAtMS0wLjItMS40Yy0wLjItMC41LTAuNC0wLjktMC44LTEuMmMtMC4zLTAuMy0wLjgtMC42LTEuMi0wLjgmIzEwOyYjOTsmIzk7Yy0wLjUtMC4yLTAuOS0wLjItMS40LTAuMkgzNC42Yy0wLjUsMC0xLDAtMS40LDAuMmMtMC41LDAuMi0wLjksMC40LTEuMiwwLjhjLTAuMywwLjMtMC42LDAuOC0wLjgsMS4yYy0wLjIsMC41LTAuMiwwLjktMC4yLDEuNCYjMTA7JiM5OyYjOTtWNzZoNTguMlYzMi41eiIgY2xhc3M9InN0NCIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" # icon_to_group_mapping defines the mapping between 'graph-icon' labels specified in the Containerlab file and the custom_styles keys. # 'graph-icon' is used within the Containerlab YAML configuration under 'labels' to visually differentiate nodes by type in the generated diagram. diff --git a/styles/nokia_bright.yaml b/styles/nokia_bright.yaml index 56ac14f..57cc9a0 100644 --- a/styles/nokia_bright.yaml +++ b/styles/nokia_bright.yaml @@ -30,8 +30,8 @@ custom_styles: default: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" spine: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" leaf: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" - dcgw: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIiB4PSIwIi8+JiN4YTs8Zz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTQ5LjcsNzBMMjAuMSw5OS44IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNOTcuNyw5Ny40TDY4LDY3LjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8L2c+JiN4YTsJPGc+JiN4YTsJCTxwYXRoIGQ9Ik03MC40LDQ5LjdMOTkuOSwyMCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8cGF0aCBkPSJNMjIuMywyMi4zTDUyLDUxLjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8cGF0aCBkPSJNMjAuMSwzMy45bDAtMTAuN2MwLTEuOCwxLjMtMywzLjEtMy4xbDEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik0zOC40LDY4bDEwLjcsMGMxLjgsMCwzLDEuMywzLjEsMy4xbDAsMTAuOCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik05OS44LDg2LjJsMCwxMC43YzAsMS44LTEuMywzLTMuMSwzLjFsLTEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik04MS44LDUxLjlsLTEwLjcsMGMtMS44LDAtMy0xLjMtMy4xLTMuMUw2OCwzOCIgY2xhc3M9InN0MSIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#ffffff;labelPosition=left;align=right;" - server: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIi8+JiN4YTs8Zz4mI3hhOwk8cGF0aCBkPSJNMTAwLDkxLjFIMjBIMTAweiBNODkuMSwzMi41YzAtMC41LDAtMS0wLjItMS40Yy0wLjItMC41LTAuNC0wLjktMC44LTEuMmMtMC4zLTAuMy0wLjgtMC42LTEuMi0wLjgmIzEwOyYjOTsmIzk7Yy0wLjUtMC4yLTAuOS0wLjItMS40LTAuMkgzNC42Yy0wLjUsMC0xLDAtMS40LDAuMmMtMC41LDAuMi0wLjksMC40LTEuMiwwLjhjLTAuMywwLjMtMC42LDAuOC0wLjgsMS4yYy0wLjIsMC41LTAuMiwwLjktMC4yLDEuNCYjMTA7JiM5OyYjOTtWNzZoNTguMlYzMi41eiIgY2xhc3M9InN0NCIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#ffffff;labelPosition=left;align=right;" + dcgw: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIiB4PSIwIi8+JiN4YTs8Zz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTQ5LjcsNzBMMjAuMSw5OS44IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNOTcuNyw5Ny40TDY4LDY3LjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8L2c+JiN4YTsJPGc+JiN4YTsJCTxwYXRoIGQ9Ik03MC40LDQ5LjdMOTkuOSwyMCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8cGF0aCBkPSJNMjIuMywyMi4zTDUyLDUxLjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8cGF0aCBkPSJNMjAuMSwzMy45bDAtMTAuN2MwLTEuOCwxLjMtMywzLjEtMy4xbDEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik0zOC40LDY4bDEwLjcsMGMxLjgsMCwzLDEuMywzLjEsMy4xbDAsMTAuOCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik05OS44LDg2LjJsMCwxMC43YzAsMS44LTEuMywzLTMuMSwzLjFsLTEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik04MS44LDUxLjlsLTEwLjcsMGMtMS44LDAtMy0xLjMtMy4xLTMuMUw2OCwzOCIgY2xhc3M9InN0MSIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" + server: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIi8+JiN4YTs8Zz4mI3hhOwk8cGF0aCBkPSJNMTAwLDkxLjFIMjBIMTAweiBNODkuMSwzMi41YzAtMC41LDAtMS0wLjItMS40Yy0wLjItMC41LTAuNC0wLjktMC44LTEuMmMtMC4zLTAuMy0wLjgtMC42LTEuMi0wLjgmIzEwOyYjOTsmIzk7Yy0wLjUtMC4yLTAuOS0wLjItMS40LTAuMkgzNC42Yy0wLjUsMC0xLDAtMS40LDAuMmMtMC41LDAuMi0wLjksMC40LTEuMiwwLjhjLTAuMywwLjMtMC42LDAuOC0wLjgsMS4yYy0wLjIsMC41LTAuMiwwLjktMC4yLDEuNCYjMTA7JiM5OyYjOTtWNzZoNTguMlYzMi41eiIgY2xhc3M9InN0NCIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" # icon_to_group_mapping defines the mapping between 'graph-icon' labels specified in the Containerlab file and the custom_styles keys. # 'graph-icon' is used within the Containerlab YAML configuration under 'labels' to visually differentiate nodes by type in the generated diagram. diff --git a/styles/nokia_dark.yaml b/styles/nokia_dark.yaml index f7bf926..454907c 100644 --- a/styles/nokia_dark.yaml +++ b/styles/nokia_dark.yaml @@ -14,6 +14,7 @@ base_style: "shape=image;imageAlign=center;imageVerticalAlign=middle;labelPositi #Style for grafana themes (only used if -g) port_style: "ellipse;whiteSpace=wrap;html=1;aspect=fixed;fontColor=#FFFFFF;fontSize=6;strokeColor=#98A2AE;fillColor=#BEC8D2;" connector_style: "ellipse;whiteSpace=wrap;html=1;aspect=fixed;fontColor=#FFFFFF;fontSize=6;fillColor=#BEC8D2;strokeColor=none;noLabel=1;" + connector_width: 12 connector_height: 12 @@ -22,16 +23,16 @@ connector_height: 12 link_style: "endArrow=none;jumpStyle=gap;strokeColor=#F0F0F0;" # Styles for labels on the source and target ends of a link -src_label_style: "edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" -trgt_label_style: "edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" +src_label_style: "edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontColor=#FFFFFF;labelBackgroundColor=#4D5766" +trgt_label_style: "edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontColor=#FFFFFF;labelBackgroundColor=#4D5766" # Custom styles for different types of nodes, allowing for unique visual representation based on node role or function custom_styles: default: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" spine: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" leaf: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwO2VkaXRhYmxlQ3NzUnVsZXM9Lio7IiB2aWV3Qm94PSIwIDAgMTIwIDEyMCIgeT0iMHB4IiB4PSIwcHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiPiYjeGE7PHN0eWxlIHR5cGU9InRleHQvY3NzIj4mI3hhOwkuc3Qwe2ZpbGw6IzAwMTEzNTt9JiN4YTsJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Qye2ZpbGw6I0ZGRkZGRjt9JiN4YTsJLnN0M3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDR7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0NXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q2e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0N3tmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q5e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTB7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxMXtmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMzt9JiN4YTsJLnN0MTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxM3tmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDE0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NC4yMzMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTsJLnN0MTV7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO30mI3hhOwkuc3QxNntmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE3e2ZpbGw6IzI2MjYyNjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTh7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9JiN4YTs8L3N0eWxlPiYjeGE7PHJlY3QgaGVpZ2h0PSIxMjAiIHdpZHRoPSIxMjAiIGNsYXNzPSJzdDAiLz4mI3hhOzxnPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNzEuNywxOS43VjQ4aDI4IiBjbGFzcz0ic3QxIi8+JiN4YTsJCTxwYXRoIGQ9Ik05MS4yLDM4LjVsNy41LDcuNmMxLjMsMS4zLDEuMywzLjEsMCw0LjNMOTEuMSw1OCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTIwLDQ3LjhoMjguNHYtMjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTM4LjgsMjguM2w3LjYtNy41YzEuMy0xLjMsMy4xLTEuMyw0LjMsMGw3LjcsNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNNDgsMTAwLjNWNzJIMjAiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTI4LjUsODEuNUwyMSw3My45Yy0xLjMtMS4zLTEuMy0zLjEsMC00LjNsNy42LTcuNyIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTEwMCw3MS45SDcxLjZ2MjgiIGNsYXNzPSJzdDEiLz4mI3hhOwkJPHBhdGggZD0iTTgxLjIsOTEuNGwtNy42LDcuNWMtMS4zLDEuMy0zLjEsMS4zLTQuMywwbC03LjctNy42IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" - dcgw: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIiB4PSIwIi8+JiN4YTs8Zz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTQ5LjcsNzBMMjAuMSw5OS44IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNOTcuNyw5Ny40TDY4LDY3LjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8L2c+JiN4YTsJPGc+JiN4YTsJCTxwYXRoIGQ9Ik03MC40LDQ5LjdMOTkuOSwyMCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8cGF0aCBkPSJNMjIuMywyMi4zTDUyLDUxLjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8cGF0aCBkPSJNMjAuMSwzMy45bDAtMTAuN2MwLTEuOCwxLjMtMywzLjEtMy4xbDEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik0zOC40LDY4bDEwLjcsMGMxLjgsMCwzLDEuMywzLjEsMy4xbDAsMTAuOCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik05OS44LDg2LjJsMCwxMC43YzAsMS44LTEuMywzLTMuMSwzLjFsLTEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik04MS44LDUxLjlsLTEwLjcsMGMtMS44LDAtMy0xLjMtMy4xLTMuMUw2OCwzOCIgY2xhc3M9InN0MSIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#ffffff;labelPosition=left;align=right;" - server: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIi8+JiN4YTs8Zz4mI3hhOwk8cGF0aCBkPSJNMTAwLDkxLjFIMjBIMTAweiBNODkuMSwzMi41YzAtMC41LDAtMS0wLjItMS40Yy0wLjItMC41LTAuNC0wLjktMC44LTEuMmMtMC4zLTAuMy0wLjgtMC42LTEuMi0wLjgmIzEwOyYjOTsmIzk7Yy0wLjUtMC4yLTAuOS0wLjItMS40LTAuMkgzNC42Yy0wLjUsMC0xLDAtMS40LDAuMmMtMC41LDAuMi0wLjksMC40LTEuMiwwLjhjLTAuMywwLjMtMC42LDAuOC0wLjgsMS4yYy0wLjIsMC41LTAuMiwwLjktMC4yLDEuNCYjMTA7JiM5OyYjOTtWNzZoNTguMlYzMi41eiIgY2xhc3M9InN0NCIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;fontColor=#ffffff;labelPosition=left;align=right;" + dcgw: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIiB4PSIwIi8+JiN4YTs8Zz4mI3hhOwk8Zz4mI3hhOwkJPHBhdGggZD0iTTQ5LjcsNzBMMjAuMSw5OS44IiBjbGFzcz0ic3QxIi8+JiN4YTsJPC9nPiYjeGE7CTxnPiYjeGE7CQk8cGF0aCBkPSJNOTcuNyw5Ny40TDY4LDY3LjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8L2c+JiN4YTsJPGc+JiN4YTsJCTxwYXRoIGQ9Ik03MC40LDQ5LjdMOTkuOSwyMCIgY2xhc3M9InN0MSIvPiYjeGE7CTwvZz4mI3hhOwk8cGF0aCBkPSJNMjIuMywyMi4zTDUyLDUxLjkiIGNsYXNzPSJzdDEiLz4mI3hhOwk8cGF0aCBkPSJNMjAuMSwzMy45bDAtMTAuN2MwLTEuOCwxLjMtMywzLjEtMy4xbDEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik0zOC40LDY4bDEwLjcsMGMxLjgsMCwzLDEuMywzLjEsMy4xbDAsMTAuOCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik05OS44LDg2LjJsMCwxMC43YzAsMS44LTEuMywzLTMuMSwzLjFsLTEwLjgsMCIgY2xhc3M9InN0MSIvPiYjeGE7CTxwYXRoIGQ9Ik04MS44LDUxLjlsLTEwLjcsMGMtMS44LDAtMy0xLjMtMy4xLTMuMUw2OCwzOCIgY2xhc3M9InN0MSIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" + server: "shape=image;editableCssRules=\\.st[0-2]$;verticalLabelPosition=top;labelBackgroundColor=none;verticalAlign=bottom;aspect=fixed;imageAspect=0;image=data:image/svg+xml,PHN2ZyB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMjAgMTIwOyIgdmlld0JveD0iMCAwIDEyMCAxMjAiIHk9IjBweCIgeD0iMHB4IiBpZD0iTGF5ZXJfMSIgdmVyc2lvbj0iMS4xIj4mI3hhOzxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+JiN4YTsJLnN0MHtmaWxsOiMwMDExMzU7fSYjeGE7CS5zdDF7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MntmaWxsOiNGRkZGRkY7fSYjeGE7CS5zdDN7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q0e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDV7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0NntmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDd7ZmlsbDpub25lO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3Q4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0OXtmaWxsOiNGRkZGRkY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7fSYjeGE7CS5zdDEwe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDt9JiN4YTsJLnN0MTF7ZmlsbDojMjYyNjI2O3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0LjIzMzM7fSYjeGE7CS5zdDEye2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9JiN4YTsJLnN0MTN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS13aWR0aDo0O30mI3hhOwkuc3QxNHtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQuMjMzMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7CS5zdDE1e2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDt9JiN4YTsJLnN0MTZ7ZmlsbDojRkZGRkZGO3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO30mI3hhOwkuc3QxN3tmaWxsOiMyNjI2MjY7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fSYjeGE7CS5zdDE4e2ZpbGw6I0ZGRkZGRjtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7fSYjeGE7PC9zdHlsZT4mI3hhOzxyZWN0IGhlaWdodD0iMTIwIiB3aWR0aD0iMTIwIiBjbGFzcz0ic3QwIi8+JiN4YTs8Zz4mI3hhOwk8cGF0aCBkPSJNMTAwLDkxLjFIMjBIMTAweiBNODkuMSwzMi41YzAtMC41LDAtMS0wLjItMS40Yy0wLjItMC41LTAuNC0wLjktMC44LTEuMmMtMC4zLTAuMy0wLjgtMC42LTEuMi0wLjgmIzEwOyYjOTsmIzk7Yy0wLjUtMC4yLTAuOS0wLjItMS40LTAuMkgzNC42Yy0wLjUsMC0xLDAtMS40LDAuMmMtMC41LDAuMi0wLjksMC40LTEuMiwwLjhjLTAuMywwLjMtMC42LDAuOC0wLjgsMS4yYy0wLjIsMC41LTAuMiwwLjktMC4yLDEuNCYjMTA7JiM5OyYjOTtWNzZoNTguMlYzMi41eiIgY2xhc3M9InN0NCIvPiYjeGE7PC9nPiYjeGE7PC9zdmc+;" # icon_to_group_mapping defines the mapping between 'graph-icon' labels specified in the Containerlab file and the custom_styles keys. # 'graph-icon' is used within the Containerlab YAML configuration under 'labels' to visually differentiate nodes by type in the generated diagram.