From ee81fc5f3fd7147396b5b8e9ee0789bee2f94e42 Mon Sep 17 00:00:00 2001 From: Benedikt Moneke <67148916+bmoneke@users.noreply.github.com> Date: Wed, 12 Jul 2023 12:39:36 +0200 Subject: [PATCH 01/15] Suggestion for message types. --- control_protocol.md | 491 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 482 insertions(+), 9 deletions(-) diff --git a/control_protocol.md b/control_protocol.md index 96e56de..3a527ea 100644 --- a/control_protocol.md +++ b/control_protocol.md @@ -330,17 +330,490 @@ A Component MUST also offer a list of all possibly callable methods in accordanc For such a RPC message, the first content frame MUST consist in a JSON-RPC compatible content, for example a single request object or a batch of request objects. +### List of methods + +List of methods defined by LECO. +You MAY implement the optional methods of this list and you MUST implement the methods obligatory for your type of Component. +All methods implemented in a Component MUST adhere to this list or MUST have a name different to any method in this list. + + +#### Component + +Any Component, i.e. any participant in the LECO protocol, MUST offer the following methods. + +::: json +{ + "openrpc": "1.2.6", + "info": { + "title": "Component", + "version": "0.1.0" + }, + "methods": [ + { + "name": "pong", + "description": "Respond to a ping.", + "params": [], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + } + ], + "components": { + "schemas": {} + } +} +::: + +Any Component MAY offer ANY of the following methods. +Components SHOULD offer ``shut_down``. + +::: json +{ + "openrpc": "1.2.6", + "info": { + "title": "Component-optional", + "version": "0.1.0" + }, + "methods": [ + { + "name": "set_log_level", + "params": [ + { + "name": "level", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "shut_down", + "params": [], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + ], + "components": { + "schemas": {} + } +} +::: + + +#### Coordinator + +Control protocol Coordinators are also {ref}`Components ` and MUST offer methods accordingly. +Furthermore, Coordinators MUST offer the following methods. + +::: json +{ + "openrpc": "1.2.6", + "info": { + "title": "Coordinator", + "version": "0.1.0" + }, + "methods": [ + { + "name": "sign_in", + "params": [], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "sign_out", + "params": [], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "coordinator_sign_in", + "params": [], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "coordinator_sign_out", + "params": [], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "set_nodes", + "params": [ + { + "name": "nodes", + "schema": { + "type": "object", + "additionalProperties": true + }, + "required": true + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "set_remote_components", + "params": [ + { + "name": "components", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "required": true + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "compose_global_directory", + "params": [], + "result": { + "name": "result", + "schema": { + "type": "object", + "additionalProperties": true + }, + "required": true + } + }, + { + "name": "compose_local_directory", + "params": [], + "result": { + "name": "result", + "schema": { + "type": "object", + "additionalProperties": true + }, + "required": true + }, + "description": "Compose a dictionary with the local directory." + }, + { + "name": "get_component_names", + "params": [], + "result": { + "name": "result", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "required": true + } + }, + { + "name": "clean_addresses", + "params": [ + { + "name": "expiration_time", + "schema": { + "type": "number" + }, + "required": true + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + }, + "description": "Clean all expired addresses from the directory.\n\n :param float expiration_time: Expiration limit in s.\n " + }, + ], + "components": { + "schemas": {} + } +} +::: + + +#### Actor + +An Actor is a {ref}`control_protocol.md#Component`. +Additionally, it MUST offer the following methods. + +::: json +{ + "openrpc": "1.2.6", + "info": { + "title": "Actor", + "version": "0.1.0" + }, + "methods": [ + { + "name": "get_parameters", + "params": [ + { + "name": "parameters", + "schema": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "required": true + } + ], + "result": { + "name": "result", + "schema": { + "type": "object", + "additionalProperties": true + }, + "required": true + } + }, + { + "name": "set_parameters", + "params": [ + { + "name": "parameters", + "schema": { + "type": "object", + "additionalProperties": true + }, + "required": true + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "call_action", + "params": [ + { + "name": "action", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "_args", + "schema": { + "anyOf": [ + { + "type": "array" + }, + { + "type": "array" + }, + { + "type": "null" + } + ] + }, + "required": false + } + ], + "result": { + "name": "result", + "schema": {}, + "required": true + } + }, + ], + "components": { + "schemas": {} + } +} +::: + + + +#### Polling Actor + +An {ref}`control_protocol.md#Actor`, which supports regular polling of values, MUST implement these methods, additionally to those of an Actor. + +::: json +{ + "openrpc": "1.2.6", + "info": { + "title": "Polling Actor", + "version": "0.1.0" + }, + "methods": [ + { + "name": "start_polling", + "params": [ + { + "name": "polling_interval", + "schema": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "required": false + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "stop_polling", + "params": [], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "get_polling_interval", + "params": [], + "result": { + "name": "result", + "schema": { + "type": "number" + }, + "required": true + } + }, + { + "name": "set_polling_interval", + "params": [ + { + "name": "polling_interval", + "schema": { + "type": "number" + }, + "required": true + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + ], + "components": { + "schemas": {} + } +} +::: + -### Messages for Transport Layer +#### Locking Actor -- SIGNIN -- SIGNOUT -- ACKNOWLEDGE -- ERROR -- PING -- CO_SIGNIN -- CO_SIGNOUT +An {ref}`control_protocol.md#Actor` which support locking resources MUST offer the following methods. :::{note} -TODO How to make these messages work? Define them directly in the transport layer? +TBD change the python code to json ::: + +::: python + def lock(self, resource: Optional[str] = None) -> bool: ... + + def unlock(self, resource: Optional[str] = None) -> None: ... + + def force_unlock(self, resource: Optional[str] = None) -> None: ... +::: + + +### Errors + +According to JSONRPC, error codes between -32000 and -32099 can be defined. +LECO defines the following errors. + +Every error has a code and a message. +Additionally they may have a ``data`` field with more information. + + +#### Routing errors + +Errors related to routing (mainly emitted by Coordinators). +They are in the range of -32000 to -32009. + + +| code | message | data | description | +|--------|------------------------------------|----------------------|--------------------------------------------------------------------------------------| +| -32000 | You did not sign in! | - | If a Component did not sign in. | +| -32001 | The name is already taken. | - | A Component tries to sign in, but another Component is signed in with the same name | +| -32002 | Node is not known. | Name of the node | The node to which the message should be sent, is not known to this Coordinator. | +| -32003 | Receiver is not in addresses list. | Name of the receiver | The Component to which the message should be sent, is not known to this Coordinator. | From baae9af3cf2944869073b021f96de26e4683a3fb Mon Sep 17 00:00:00 2001 From: Benedikt Moneke <67148916+bmoneke@users.noreply.github.com> Date: Thu, 20 Jul 2023 10:20:08 +0200 Subject: [PATCH 02/15] Adjust error code ranges and numbers. --- control_protocol.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/control_protocol.md b/control_protocol.md index 3a527ea..c614f24 100644 --- a/control_protocol.md +++ b/control_protocol.md @@ -798,22 +798,22 @@ TBD change the python code to json ### Errors -According to JSONRPC, error codes between -32000 and -32099 can be defined. -LECO defines the following errors. - Every error has a code and a message. Additionally they may have a ``data`` field with more information. +According to JSONRPC, applications can define error codes between -32000 and -32099. +LECO defines the following errors. + #### Routing errors Errors related to routing (mainly emitted by Coordinators). -They are in the range of -32000 to -32009. +Their error codes are in the range of -32090 to -32099. | code | message | data | description | |--------|------------------------------------|----------------------|--------------------------------------------------------------------------------------| -| -32000 | You did not sign in! | - | If a Component did not sign in. | -| -32001 | The name is already taken. | - | A Component tries to sign in, but another Component is signed in with the same name | -| -32002 | Node is not known. | Name of the node | The node to which the message should be sent, is not known to this Coordinator. | -| -32003 | Receiver is not in addresses list. | Name of the receiver | The Component to which the message should be sent, is not known to this Coordinator. | +| -32090 | You did not sign in! | - | If a Component did not sign in. | +| -32091 | The name is already taken. | - | A Component tries to sign in, but another Component is signed in with the same name | +| -32092 | Node is not known. | Name of the node | The node to which the message should be sent, is not known to this Coordinator. | +| -32093 | Receiver is not in addresses list. | Name of the receiver | The Component to which the message should be sent, is not known to this Coordinator. | From 5ae6e5b110e7ea38575be48ec146510339d44a56 Mon Sep 17 00:00:00 2001 From: Benedikt Moneke <67148916+bmoneke@users.noreply.github.com> Date: Mon, 24 Jul 2023 13:27:13 +0200 Subject: [PATCH 03/15] Error messages improved. Co-authored-by: Christoph Buchner --- control_protocol.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/control_protocol.md b/control_protocol.md index c614f24..dc73d00 100644 --- a/control_protocol.md +++ b/control_protocol.md @@ -813,7 +813,7 @@ Their error codes are in the range of -32090 to -32099. | code | message | data | description | |--------|------------------------------------|----------------------|--------------------------------------------------------------------------------------| -| -32090 | You did not sign in! | - | If a Component did not sign in. | +| -32090 | Component not signed in yet! | Name of the component| If a Component did not sign in. | | -32091 | The name is already taken. | - | A Component tries to sign in, but another Component is signed in with the same name | -| -32092 | Node is not known. | Name of the node | The node to which the message should be sent, is not known to this Coordinator. | +| -32092 | Node is unknown. | Name of the node | The node to which the message should be sent, is not known to this Coordinator. | | -32093 | Receiver is not in addresses list. | Name of the receiver | The Component to which the message should be sent, is not known to this Coordinator. | From 28816469816aa4b1b7b003ac5a95c8993dde7257 Mon Sep 17 00:00:00 2001 From: Benedikt Moneke <67148916+bmoneke@users.noreply.github.com> Date: Mon, 24 Jul 2023 17:10:48 +0200 Subject: [PATCH 04/15] Moving schemas to their own files and use sphinx-jsonschema. --- conf.py | 2 +- control_protocol.md | 417 +------------------------------- environment.yml | 1 + schemas/actor.json | 122 ++++++++++ schemas/component.json | 21 ++ schemas/component_optional.json | 45 ++++ schemas/coordinator.json | 211 ++++++++++++++++ schemas/polling_actor.json | 77 ++++++ 8 files changed, 483 insertions(+), 413 deletions(-) create mode 100644 schemas/actor.json create mode 100644 schemas/component.json create mode 100644 schemas/component_optional.json create mode 100644 schemas/coordinator.json create mode 100644 schemas/polling_actor.json diff --git a/conf.py b/conf.py index 5a1d0c4..a9350c4 100644 --- a/conf.py +++ b/conf.py @@ -13,7 +13,7 @@ # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -extensions = ['myst_parser', 'sphinxcontrib.mermaid', 'sphinx_rtd_theme'] +extensions = ['myst_parser', 'sphinxcontrib.mermaid', 'sphinx_rtd_theme', 'sphinx.ext.autodoc', 'sphinx-jsonschema'] myst_enable_extensions = [ "colon_fence", diff --git a/control_protocol.md b/control_protocol.md index dc73d00..621413b 100644 --- a/control_protocol.md +++ b/control_protocol.md @@ -341,80 +341,12 @@ All methods implemented in a Component MUST adhere to this list or MUST have a n Any Component, i.e. any participant in the LECO protocol, MUST offer the following methods. -::: json -{ - "openrpc": "1.2.6", - "info": { - "title": "Component", - "version": "0.1.0" - }, - "methods": [ - { - "name": "pong", - "description": "Respond to a ping.", - "params": [], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - } - } - ], - "components": { - "schemas": {} - } -} -::: +.. jsonschema:: schemas/actor.json Any Component MAY offer ANY of the following methods. Components SHOULD offer ``shut_down``. -::: json -{ - "openrpc": "1.2.6", - "info": { - "title": "Component-optional", - "version": "0.1.0" - }, - "methods": [ - { - "name": "set_log_level", - "params": [ - { - "name": "level", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - } - }, - { - "name": "shut_down", - "params": [], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - } - }, - ], - "components": { - "schemas": {} - } -} -::: +.. jsonschema:: schemas/component_optional.json #### Coordinator @@ -422,165 +354,7 @@ Components SHOULD offer ``shut_down``. Control protocol Coordinators are also {ref}`Components ` and MUST offer methods accordingly. Furthermore, Coordinators MUST offer the following methods. -::: json -{ - "openrpc": "1.2.6", - "info": { - "title": "Coordinator", - "version": "0.1.0" - }, - "methods": [ - { - "name": "sign_in", - "params": [], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - } - }, - { - "name": "sign_out", - "params": [], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - } - }, - { - "name": "coordinator_sign_in", - "params": [], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - } - }, - { - "name": "coordinator_sign_out", - "params": [], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - } - }, - { - "name": "set_nodes", - "params": [ - { - "name": "nodes", - "schema": { - "type": "object", - "additionalProperties": true - }, - "required": true - } - ], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - } - }, - { - "name": "set_remote_components", - "params": [ - { - "name": "components", - "schema": { - "type": "array", - "items": { - "type": "string" - } - }, - "required": true - } - ], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - } - }, - { - "name": "compose_global_directory", - "params": [], - "result": { - "name": "result", - "schema": { - "type": "object", - "additionalProperties": true - }, - "required": true - } - }, - { - "name": "compose_local_directory", - "params": [], - "result": { - "name": "result", - "schema": { - "type": "object", - "additionalProperties": true - }, - "required": true - }, - "description": "Compose a dictionary with the local directory." - }, - { - "name": "get_component_names", - "params": [], - "result": { - "name": "result", - "schema": { - "type": "array", - "items": { - "type": "string" - } - }, - "required": true - } - }, - { - "name": "clean_addresses", - "params": [ - { - "name": "expiration_time", - "schema": { - "type": "number" - }, - "required": true - } - ], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - }, - "description": "Clean all expired addresses from the directory.\n\n :param float expiration_time: Expiration limit in s.\n " - }, - ], - "components": { - "schemas": {} - } -} -::: +.. jsonschema:: schemas/coordinator.json #### Actor @@ -588,195 +362,14 @@ Furthermore, Coordinators MUST offer the following methods. An Actor is a {ref}`control_protocol.md#Component`. Additionally, it MUST offer the following methods. -::: json -{ - "openrpc": "1.2.6", - "info": { - "title": "Actor", - "version": "0.1.0" - }, - "methods": [ - { - "name": "get_parameters", - "params": [ - { - "name": "parameters", - "schema": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "required": true - } - ], - "result": { - "name": "result", - "schema": { - "type": "object", - "additionalProperties": true - }, - "required": true - } - }, - { - "name": "set_parameters", - "params": [ - { - "name": "parameters", - "schema": { - "type": "object", - "additionalProperties": true - }, - "required": true - } - ], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - } - }, - { - "name": "call_action", - "params": [ - { - "name": "action", - "schema": { - "type": "string" - }, - "required": true - }, - { - "name": "_args", - "schema": { - "anyOf": [ - { - "type": "array" - }, - { - "type": "array" - }, - { - "type": "null" - } - ] - }, - "required": false - } - ], - "result": { - "name": "result", - "schema": {}, - "required": true - } - }, - ], - "components": { - "schemas": {} - } -} -::: - +.. jsonschema:: schemas/actor.schema #### Polling Actor An {ref}`control_protocol.md#Actor`, which supports regular polling of values, MUST implement these methods, additionally to those of an Actor. -::: json -{ - "openrpc": "1.2.6", - "info": { - "title": "Polling Actor", - "version": "0.1.0" - }, - "methods": [ - { - "name": "start_polling", - "params": [ - { - "name": "polling_interval", - "schema": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] - }, - "required": false - } - ], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - } - }, - { - "name": "stop_polling", - "params": [], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - } - }, - { - "name": "get_polling_interval", - "params": [], - "result": { - "name": "result", - "schema": { - "type": "number" - }, - "required": true - } - }, - { - "name": "set_polling_interval", - "params": [ - { - "name": "polling_interval", - "schema": { - "type": "number" - }, - "required": true - } - ], - "result": { - "name": "result", - "schema": { - "type": "null" - }, - "required": true - } - }, - ], - "components": { - "schemas": {} - } -} -::: +.. jsonschema:: schemas/polling_actor.json #### Locking Actor diff --git a/environment.yml b/environment.yml index ef2ff56..9ee907f 100644 --- a/environment.yml +++ b/environment.yml @@ -7,3 +7,4 @@ dependencies: - sphinx=5.3.0 - sphinxcontrib-mermaid=0.8.1 - sphinx_rtd_theme=1.1.1 + - sphinx-jsonschema=1.19.1 diff --git a/schemas/actor.json b/schemas/actor.json new file mode 100644 index 0000000..f9590b8 --- /dev/null +++ b/schemas/actor.json @@ -0,0 +1,122 @@ +{ + "openrpc": "1.2.6", + "info": { + "title": "Actor", + "version": "0.1.0" + }, + "methods": [ + { + "name": "get_parameters", + "params": [ + { + "name": "parameters", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "required": true + } + ], + "description": "Get the values for parameters.", + "result": { + "name": "result", + "schema": { + "$ref": "#/components/parameter_value_list" + }, + "required": true + } + }, + { + "name": "set_parameters", + "params": [ + { + "name": "parameters", + "schema": { + "$ref": "#/components/parameter_value_list" + }, + "required": true + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "call_action", + "description": "Call an Action of this Component. All other arguments are handed to the action.", + "params": [ + { + "name": "action", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "args", + "schema": { + "type": "array", + "description": "List of positional arguments.", + "items": { + "$ref": "#/components/any" + } + }, + "required": false + } + ], + "result": { + "name": "result", + "description": "Result of the action.", + "schema": { + "$ref": "#/components/any" + }, + "required": true + } + } + ], + "components": { + "parameter_value_list": { + "description": "List of parameters and their values.", + "type": "array", + "items": { + "name": "parameter_value", + "properties": { + "parameter": { + "type": "string", + "description": "Name of the parameter." + }, + "value": { + "$ref": "#/components/any" + } + }, + "required": [ "parameter", "value" ] + } + }, + "any" : { + "anyOf": [ + { + "description": "Value of the parameter.", + "type": "integer" + }, + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "null" + }, + { + "type": "object" + } + ] + } + } +} diff --git a/schemas/component.json b/schemas/component.json new file mode 100644 index 0000000..9342b88 --- /dev/null +++ b/schemas/component.json @@ -0,0 +1,21 @@ +{ + "openrpc": "1.2.6", + "info": { + "title": "Component", + "version": "0.1.0" + }, + "methods": [ + { + "name": "pong", + "description": "Respond to a ping.", + "params": [], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + } + ] +} diff --git a/schemas/component_optional.json b/schemas/component_optional.json new file mode 100644 index 0000000..1582365 --- /dev/null +++ b/schemas/component_optional.json @@ -0,0 +1,45 @@ +{ + "openrpc": "1.2.6", + "info": { + "title": "Component-optional", + "version": "0.1.0" + }, + "methods": [ + { + "name": "set_log_level", + "params": [ + { + "name": "level", + "schema": { + "type": "integer", + "multipleOf": 10, + "minimum": 0, + "maximum": 100 + }, + "required": true + } + ], + "description": "Set the logging level.", + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "shut_down", + "params": [], + "description": "Shut down the Component.", + "result": { + "name": "result", + "schema": { + + "type": "null" + }, + "required": true + } + } + ] +} diff --git a/schemas/coordinator.json b/schemas/coordinator.json new file mode 100644 index 0000000..b8df7e4 --- /dev/null +++ b/schemas/coordinator.json @@ -0,0 +1,211 @@ +{ + "openrpc": "1.2.6", + "info": { + "title": "Coordinator", + "description": "Coordinates messages between LECO Components.", + "version": "0.1.0" + }, + "methods": [ + { + "name": "sign_in", + "params": [], + "description": "Sign in to this Coordinator.", + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + }, + "errors": [ + { + "code": -32091, + "message": "The name is already taken.", + "data": "Already taken name." + } + ] + }, + { + "name": "sign_out", + "params": [], + "description": "Sign out from this Coordinator.", + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "coordinator_sign_in", + "params": [], + "description": "Sign in as a Coordinator to this Coordinator.", + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "coordinator_sign_out", + "params": [], + "description": "Sign out as a Coordinator from this Coordinator.", + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "set_nodes", + "params": [ + { + "name": "nodes", + "schema": { + "$ref": "#/components/nodes" + } + } + ], + "description": "Set node addresses and connect to all nodes, to which there is not yet any connection.", + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "get_nodes", + "params": [], + "description": "Get the nodes known to this Coordinator.", + "result": { + "name": "result", + "schema": { + "$ref": "#/components/nodes" + }, + "required": true + } + }, + { + "name": "set_components", + "params": [ + { + "name": "components", + "description": "List of Components' names.", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "required": true + } + ], + "description": "Update Directory with Components of another Coordinator.", + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "get_local_components", + "params": [], + "description": "Get the locally connected Components.", + "result": { + "name": "result", + "schema": { + "$ref": "#/components/components_list" + }, + "required": true + } + }, + { + "name": "get_global_components", + "params": [], + "description": "Get all Components in this LECO network.", + "result": { + "name": "result", + "schema": { + "$ref": "#/components/global_directory" + } + } + }, + { + "name": "clean_addresses", + "params": [ + { + "name": "expiration_time", + "description": "Expiration time of Components and Coordinators in s.", + "summary": "seconds", + "schema": { + "type": "number" + }, + "required": true + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + }, + "description": "Clean all expired addresses from the directory." + } + ], + "components": { + "global_directory": { + "type": "array", + "description": "List of Namespaces and their Components.", + "items": { + "type": "object", + "description": "Namespace with connected Components.", + "properties": { + "namespace": "string", + "components": { + "$ref": "#/components/components_list" + } + }, + "required": [ "namespace" ] + } + }, + "nodes" : { + "type": "array", + "description": "List of nodes and their adresses.", + "items": { + "$ref": "#/components/node" + } + }, + "node": { + "name": "node", + "type": "object", + "properties": { + "address": { + "type": "string", + "description": "Address at which the Coordinator can be reached, e.g. TCP address." + }, + "namespace": { + "type": "string", + "description": "Namespace of the node." + } + }, + "required": [ "address", "namespace" ] + }, + "components_list": { + "type": "array", + "description": "List of Components' names.", + "items": { + "type": "string" + } + } + } +} diff --git a/schemas/polling_actor.json b/schemas/polling_actor.json new file mode 100644 index 0000000..1bfe5a6 --- /dev/null +++ b/schemas/polling_actor.json @@ -0,0 +1,77 @@ +{ + "openrpc": "1.2.6", + "info": { + "title": "Polling Actor", + "version": "0.1.0" + }, + "methods": [ + { + "name": "start_polling", + "description": "Start to poll some values regularly.", + "params": [ + { + "name": "polling_interval", + "summary": "seconds", + "description": "Polling interval in seconds.", + "schema": { + "type": "number" + }, + "required": false + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "stop_polling", + "description": "Stop to poll regularly.", + "params": [], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + }, + { + "name": "get_polling_interval", + "description": "Get the polling interval.", + "params": [], + "result": { + "name": "result", + "description": "The polling interval in seconds.", + "schema": { + "type": "number" + }, + "required": true + } + }, + { + "name": "set_polling_interval", + "description": "Set the polling interval", + "params": [ + { + "name": "polling_interval", + "description": "Polling interval in seconds.", + "schema": { + "type": "number" + }, + "required": true + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + }, + "required": true + } + } + ] +} From 4254a2b788ac5bf5fc82aca984170f572badc992 Mon Sep 17 00:00:00 2001 From: Benedikt Moneke <67148916+bmoneke@users.noreply.github.com> Date: Mon, 24 Jul 2023 18:04:10 +0200 Subject: [PATCH 05/15] Rename methods. --- conf.py | 12 +++++++++--- control_protocol.md | 12 ++++++------ schemas/coordinator.json | 27 +++++++++++++-------------- schemas/polling_actor.json | 1 - 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/conf.py b/conf.py index a9350c4..3868bac 100644 --- a/conf.py +++ b/conf.py @@ -7,13 +7,19 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = 'experiment_control_protocol' -copyright = '2023, PyMeasure maintainers' -author = 'PyMeasure maintainers' +copyright = '2023, LECO maintainers' +author = 'LECO maintainers' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -extensions = ['myst_parser', 'sphinxcontrib.mermaid', 'sphinx_rtd_theme', 'sphinx.ext.autodoc', 'sphinx-jsonschema'] +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx-jsonschema', + 'myst_parser', + 'sphinxcontrib.mermaid', + 'sphinx_rtd_theme', +] myst_enable_extensions = [ "colon_fence", diff --git a/control_protocol.md b/control_protocol.md index 621413b..2acaddc 100644 --- a/control_protocol.md +++ b/control_protocol.md @@ -339,7 +339,7 @@ All methods implemented in a Component MUST adhere to this list or MUST have a n #### Component -Any Component, i.e. any participant in the LECO protocol, MUST offer the following methods. +Any Component, i.e. any participant in the LECO protocol, MUST offer the [OpenRPC Service Discovery Method](https://spec.open-rpc.org/#service-discovery-method) and the following methods. .. jsonschema:: schemas/actor.json @@ -351,7 +351,7 @@ Components SHOULD offer ``shut_down``. #### Coordinator -Control protocol Coordinators are also {ref}`Components ` and MUST offer methods accordingly. +Control protocol Coordinators are also {ref}`Components `. Furthermore, Coordinators MUST offer the following methods. .. jsonschema:: schemas/coordinator.json @@ -367,7 +367,7 @@ Additionally, it MUST offer the following methods. #### Polling Actor -An {ref}`control_protocol.md#Actor`, which supports regular polling of values, MUST implement these methods, additionally to those of an Actor. +An {ref}`control_protocol.md#Actor`, which supports regular polling of values, MUST implement these methods. .. jsonschema:: schemas/polling_actor.json @@ -406,7 +406,7 @@ Their error codes are in the range of -32090 to -32099. | code | message | data | description | |--------|------------------------------------|----------------------|--------------------------------------------------------------------------------------| -| -32090 | Component not signed in yet! | Name of the component| If a Component did not sign in. | -| -32091 | The name is already taken. | - | A Component tries to sign in, but another Component is signed in with the same name | -| -32092 | Node is unknown. | Name of the node | The node to which the message should be sent, is not known to this Coordinator. | +| -32090 | Component not signed in yet! | Name of the Component| If a Component did not sign in. | +| -32091 | The name is already taken. | Name of the Component| A Component tries to sign in, but another Component is signed in with the same name | +| -32092 | Node is unknown. | Name of the Node | The Node to which the message should be sent, is not known to this Coordinator. | | -32093 | Receiver is not in addresses list. | Name of the receiver | The Component to which the message should be sent, is not known to this Coordinator. | diff --git a/schemas/coordinator.json b/schemas/coordinator.json index b8df7e4..eaa8f15 100644 --- a/schemas/coordinator.json +++ b/schemas/coordinator.json @@ -62,7 +62,8 @@ } }, { - "name": "set_nodes", + "name": "add_nodes", + "description": "Add nodes, to which there is not yet any connection.", "params": [ { "name": "nodes", @@ -71,7 +72,6 @@ } } ], - "description": "Set node addresses and connect to all nodes, to which there is not yet any connection.", "result": { "name": "result", "schema": { @@ -81,9 +81,9 @@ } }, { - "name": "get_nodes", + "name": "Send_nodes", "params": [], - "description": "Get the nodes known to this Coordinator.", + "description": "Send the names and addresses of the nodes known to this Coordinator.", "result": { "name": "result", "schema": { @@ -93,7 +93,8 @@ } }, { - "name": "set_components", + "name": "record_components", + "description": "Record Components of another Coordinator.", "params": [ { "name": "components", @@ -107,7 +108,6 @@ "required": true } ], - "description": "Update Directory with Components of another Coordinator.", "result": { "name": "result", "schema": { @@ -117,9 +117,9 @@ } }, { - "name": "get_local_components", + "name": "send_local_components", "params": [], - "description": "Get the locally connected Components.", + "description": "Send the names of locally connected Components.", "result": { "name": "result", "schema": { @@ -129,9 +129,9 @@ } }, { - "name": "get_global_components", + "name": "send_global_components", "params": [], - "description": "Get all Components in this LECO network.", + "description": "Send the names of all Components in this LECO network.", "result": { "name": "result", "schema": { @@ -140,12 +140,12 @@ } }, { - "name": "clean_addresses", + "name": "remove_expired_adresses", + "description": "Clean all expired addresses from the directory." "params": [ { "name": "expiration_time", - "description": "Expiration time of Components and Coordinators in s.", - "summary": "seconds", + "description": "Expiration time of Components and Coordinators in seconds.", "schema": { "type": "number" }, @@ -159,7 +159,6 @@ }, "required": true }, - "description": "Clean all expired addresses from the directory." } ], "components": { diff --git a/schemas/polling_actor.json b/schemas/polling_actor.json index 1bfe5a6..2fc9700 100644 --- a/schemas/polling_actor.json +++ b/schemas/polling_actor.json @@ -11,7 +11,6 @@ "params": [ { "name": "polling_interval", - "summary": "seconds", "description": "Polling interval in seconds.", "schema": { "type": "number" From 9ed49b273a83d64383c1b48b9d0dbe5f9c0fc710 Mon Sep 17 00:00:00 2001 From: Benedikt Moneke <67148916+bmoneke@users.noreply.github.com> Date: Mon, 24 Jul 2023 19:24:22 +0200 Subject: [PATCH 06/15] Switching to dictionaries, examples added. --- schemas/actor.json | 95 +++++++++++++++++++++------- schemas/coordinator.json | 131 +++++++++++++++++++++++---------------- 2 files changed, 151 insertions(+), 75 deletions(-) diff --git a/schemas/actor.json b/schemas/actor.json index f9590b8..d0fb400 100644 --- a/schemas/actor.json +++ b/schemas/actor.json @@ -7,6 +7,7 @@ "methods": [ { "name": "get_parameters", + "description": "Get the values for parameters.", "params": [ { "name": "parameters", @@ -19,14 +20,33 @@ "required": true } ], - "description": "Get the values for parameters.", "result": { "name": "result", "schema": { - "$ref": "#/components/parameter_value_list" + "type": "object", + "description": "Object with parameter-value pairs", + "additionalProperties": true }, "required": true - } + }, + "examples": [ + { + "name": "Get parameters example", + "params": [ + { + "name": "parameters", + "value": ["parameter1", "parameter2"] + } + ], + "result": { + "name": "result", + "value": { + "parameter1": "value_of_parameter1", + "parameter2": "some_other_value" + } + } + } + ] }, { "name": "set_parameters", @@ -34,7 +54,10 @@ { "name": "parameters", "schema": { - "$ref": "#/components/parameter_value_list" + "type": "object", + "description": "Object with parameter-value pairs", + "additionalProperties": true + }, "required": true } @@ -45,7 +68,26 @@ "type": "null" }, "required": true - } + }, + "examples": [ + { + "name": "Set parameters example", + "params": [ + { + "name": "parameters", + "value": { + "parameter1": "value_of_parameter1", + "parameter2": "some_other_value" + } + } + ], + "result": { + "name": "result", + "value": null + } + } + ] + }, { "name": "call_action", @@ -77,27 +119,34 @@ "$ref": "#/components/any" }, "required": true - } + }, + "examples": [ + { + "name": "Calling some action with keyword arguments only.", + "params": [ + { + "name": "action", + "value": "super_action" + }, + { + "name": "args", + "value": null + + }, + { + "name": "argument1", + "value": 5 + } + ], + "result": { + "name": "result", + "value": "result of 'super_action(argument1=5)'." + } + } + ] } ], "components": { - "parameter_value_list": { - "description": "List of parameters and their values.", - "type": "array", - "items": { - "name": "parameter_value", - "properties": { - "parameter": { - "type": "string", - "description": "Name of the parameter." - }, - "value": { - "$ref": "#/components/any" - } - }, - "required": [ "parameter", "value" ] - } - }, "any" : { "anyOf": [ { diff --git a/schemas/coordinator.json b/schemas/coordinator.json index eaa8f15..cc8981c 100644 --- a/schemas/coordinator.json +++ b/schemas/coordinator.json @@ -78,10 +78,28 @@ "type": "null" }, "required": true - } + }, + "examples": [ + { + "name": "Add nodes example", + "params": [ + { + "name": "nodes", + "value": { + "Namespace1": "host_of_namespace1:12345", + "Namespace2": "host_of_namespace2:54321" + } + } + ], + "result": { + "name": "result", + "value": null + } + } + ] }, { - "name": "Send_nodes", + "name": "send_nodes", "params": [], "description": "Send the names and addresses of the nodes known to this Coordinator.", "result": { @@ -90,7 +108,20 @@ "$ref": "#/components/nodes" }, "required": true - } + }, + "examples": [ + { + "name": "Send nodes example", + "params": [], + "result": { + "name": "result", + "value": { + "Namespace1": "host_of_namespace1:12345", + "Namespace2": "host_of_namespace2:54321" + } + } + } + ] }, { "name": "record_components", @@ -100,10 +131,7 @@ "name": "components", "description": "List of Components' names.", "schema": { - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/components/components_array" }, "required": true } @@ -123,10 +151,23 @@ "result": { "name": "result", "schema": { - "$ref": "#/components/components_list" + "$ref": "#/components/components_array" }, "required": true - } + }, + "examples": [ + { + "name": "Send local directory example", + "params": [], + "result": { + "name": "result", + "value": [ + "ComponentA", + "ComponentB" + ] + } + } + ] }, { "name": "send_global_components", @@ -135,13 +176,31 @@ "result": { "name": "result", "schema": { - "$ref": "#/components/global_directory" + "type": "object", + "description": "Object with 'namespace'-'array of component names' pairs.", + "additionalProperties": { + "$ref": "#/components/components_array" + } } - } + }, + "examples": [ + { + "name": "Global directory example.", + "params": [], + "result": { + "name": "result", + "value": { + "Namespace1": ["Namespace1.ComponentA", "Namespace1.ComponentB"], + "Namespace2": ["Namespace2.ComponentC", "Namespace2.ComponentD", "Namespace2.ComponentE"], + "Namespace3": ["Namespace3.ComponentF"] + } + } + } + ] }, { "name": "remove_expired_adresses", - "description": "Clean all expired addresses from the directory." + "description": "Clean all expired addresses from the directory.", "params": [ { "name": "expiration_time", @@ -158,51 +217,19 @@ "type": "null" }, "required": true - }, + } } ], "components": { - "global_directory": { - "type": "array", - "description": "List of Namespaces and their Components.", - "items": { + "nodes" : { "type": "object", - "description": "Namespace with connected Components.", - "properties": { - "namespace": "string", - "components": { - "$ref": "#/components/components_list" - } - }, - "required": [ "namespace" ] - } - }, - "nodes" : { - "type": "array", - "description": "List of nodes and their adresses.", - "items": { - "$ref": "#/components/node" - } - }, - "node": { - "name": "node", - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "Address at which the Coordinator can be reached, e.g. TCP address." - }, - "namespace": { - "type": "string", - "description": "Namespace of the node." - } + "additionalProperties": {"type": "string"}, + "description": "Object with 'node'-'address' pairs as properties." }, - "required": [ "address", "namespace" ] - }, - "components_list": { - "type": "array", - "description": "List of Components' names.", - "items": { + "components_array": { + "type": "array", + "description": "List of Components' names.", + "items": { "type": "string" } } From d0569706c2c19edfcd89b90a611b662d4e7b75b6 Mon Sep 17 00:00:00 2001 From: Benedikt Moneke <67148916+bmoneke@users.noreply.github.com> Date: Tue, 5 Sep 2023 09:11:55 +0200 Subject: [PATCH 07/15] Reword add_notes description --- schemas/coordinator.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/coordinator.json b/schemas/coordinator.json index cc8981c..995e7e5 100644 --- a/schemas/coordinator.json +++ b/schemas/coordinator.json @@ -63,7 +63,7 @@ }, { "name": "add_nodes", - "description": "Add nodes, to which there is not yet any connection.", + "description": "Establish a connection to these nodes, if not yet connected.", "params": [ { "name": "nodes", From 9d9a6d78b91ed23440ee9ae7033f5a5b3e53ae2f Mon Sep 17 00:00:00 2001 From: Benedikt Burger <67148916+bmoneke@users.noreply.github.com> Date: Tue, 26 Sep 2023 15:42:17 +0200 Subject: [PATCH 08/15] Changing to data-view. --- Hello_world.md | 19 +++++++++++++++++++ conf.py | 2 +- control_protocol.md | 25 ++++++++++++++++++++----- environment.yml | 2 +- schemas/actor.json | 1 - schemas/coordinator.json | 9 ++++++++- 6 files changed, 49 insertions(+), 9 deletions(-) diff --git a/Hello_world.md b/Hello_world.md index facfec8..b5e6188 100644 --- a/Hello_world.md +++ b/Hello_world.md @@ -19,6 +19,8 @@ I can also reference the section {ref}`section-two` without specifying my title. :::{note} And here's a note with a colon fence! + +Note, that you have to use a colon fence in a markdown file instead of the normal directives in rst files. ::: And finally, here's a cool mermaid diagram! @@ -36,3 +38,20 @@ sequenceDiagram John->Bob: How about you? Bob-->John: Jolly good! ::: + + + +## JSON Schema + +You can show json data in a data-viewer: + +:::{data-viewer} +:expand: +:file: schemas/component.json +::: + + + + + +test diff --git a/conf.py b/conf.py index 3868bac..1d263b8 100644 --- a/conf.py +++ b/conf.py @@ -15,10 +15,10 @@ extensions = [ 'sphinx.ext.autodoc', - 'sphinx-jsonschema', 'myst_parser', 'sphinxcontrib.mermaid', 'sphinx_rtd_theme', + "sphinx_data_viewer", ] myst_enable_extensions = [ diff --git a/control_protocol.md b/control_protocol.md index 2acaddc..9c972cf 100644 --- a/control_protocol.md +++ b/control_protocol.md @@ -341,12 +341,18 @@ All methods implemented in a Component MUST adhere to this list or MUST have a n Any Component, i.e. any participant in the LECO protocol, MUST offer the [OpenRPC Service Discovery Method](https://spec.open-rpc.org/#service-discovery-method) and the following methods. -.. jsonschema:: schemas/actor.json +:::{data-viewer} +:expand: +:file: schemas/component.json +::: Any Component MAY offer ANY of the following methods. Components SHOULD offer ``shut_down``. -.. jsonschema:: schemas/component_optional.json +:::{data-viewer} +:expand: +:file: schemas/component_optional.json +::: #### Coordinator @@ -354,7 +360,10 @@ Components SHOULD offer ``shut_down``. Control protocol Coordinators are also {ref}`Components `. Furthermore, Coordinators MUST offer the following methods. -.. jsonschema:: schemas/coordinator.json +:::{data-viewer} +:expand: +:file: schemas/coordinator.json +::: #### Actor @@ -362,14 +371,20 @@ Furthermore, Coordinators MUST offer the following methods. An Actor is a {ref}`control_protocol.md#Component`. Additionally, it MUST offer the following methods. -.. jsonschema:: schemas/actor.schema +:::{data-viewer} +:expand: +:file: schemas/actor.json +::: #### Polling Actor An {ref}`control_protocol.md#Actor`, which supports regular polling of values, MUST implement these methods. -.. jsonschema:: schemas/polling_actor.json +:::{data-viewer} +:expand: +:file: schemas/polling_actor.json +::: #### Locking Actor diff --git a/environment.yml b/environment.yml index 9ee907f..0296c9c 100644 --- a/environment.yml +++ b/environment.yml @@ -7,4 +7,4 @@ dependencies: - sphinx=5.3.0 - sphinxcontrib-mermaid=0.8.1 - sphinx_rtd_theme=1.1.1 - - sphinx-jsonschema=1.19.1 + - sphinx_data_viewer=0.1.2 diff --git a/schemas/actor.json b/schemas/actor.json index d0fb400..39aa4cf 100644 --- a/schemas/actor.json +++ b/schemas/actor.json @@ -150,7 +150,6 @@ "any" : { "anyOf": [ { - "description": "Value of the parameter.", "type": "integer" }, { diff --git a/schemas/coordinator.json b/schemas/coordinator.json index 995e7e5..458927a 100644 --- a/schemas/coordinator.json +++ b/schemas/coordinator.json @@ -47,7 +47,14 @@ "type": "null" }, "required": true - } + }, + "errors": [ + { + "code": -32091, + "message": "The name is already taken.", + "data": "Already taken name." + } + ] }, { "name": "coordinator_sign_out", From 50f38494104bf71ba426f5af5d4398e370440548 Mon Sep 17 00:00:00 2001 From: Benedikt Burger <67148916+bmoneke@users.noreply.github.com> Date: Wed, 27 Sep 2023 11:16:32 +0200 Subject: [PATCH 09/15] Add locking actor. --- control_protocol.md | 22 +++--- schemas/locking_actor.json | 144 +++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 9 deletions(-) create mode 100644 schemas/locking_actor.json diff --git a/control_protocol.md b/control_protocol.md index 9c972cf..e97d8e3 100644 --- a/control_protocol.md +++ b/control_protocol.md @@ -391,17 +391,12 @@ An {ref}`control_protocol.md#Actor`, which supports regular polling of values, M An {ref}`control_protocol.md#Actor` which support locking resources MUST offer the following methods. -:::{note} -TBD change the python code to json +:::{data-viewer} +:expand: +:file: schemas/locking_actor.json ::: -::: python - def lock(self, resource: Optional[str] = None) -> bool: ... - - def unlock(self, resource: Optional[str] = None) -> None: ... - - def force_unlock(self, resource: Optional[str] = None) -> None: ... -::: +Accessing a locked resource (the whole Component or parts of it) or trying to unlock one, locked by another Component, will raise {ref}`control_protocol.md#locking_errors`. ### Errors @@ -425,3 +420,12 @@ Their error codes are in the range of -32090 to -32099. | -32091 | The name is already taken. | Name of the Component| A Component tries to sign in, but another Component is signed in with the same name | | -32092 | Node is unknown. | Name of the Node | The Node to which the message should be sent, is not known to this Coordinator. | | -32093 | Receiver is not in addresses list. | Name of the receiver | The Component to which the message should be sent, is not known to this Coordinator. | + + +#### Locking errors + +Errors related to locked Resources + +| code | message | data | description | +|--------|------------------|------|----------------------------------------------| +| -32050 | Resource locked! | - | The resource is locked by another component. | diff --git a/schemas/locking_actor.json b/schemas/locking_actor.json new file mode 100644 index 0000000..46e8259 --- /dev/null +++ b/schemas/locking_actor.json @@ -0,0 +1,144 @@ +{ + "openrpc": "1.2.6", + "info": { + "title": "Locking Actor", + "version": "0.1.0" + }, + "methods": [ + { + "name": "lock", + "description": "Lock this Component or a resource of it, such that no other Component may use it.", + "params": [ + { + "name": "resource", + "schema": { + "type": "string" + }, + "required": false + } + ], + "result": { + "name": "result", + "schema": { + "type": "boolean", + "description": "Whether locking succeeded or not." + }, + "required": true + }, + "examples": [ + { + "name": "Lock the 'channel1' resource of the Component", + "params": [ + { + "name": "resource", + "value": "channel1" + } + ], + "result": { + "name": "result", + "value": true + } + }, + { + "name": "Lock Component example", + "params": [ + ], + "result": { + "name": "result", + "value": true + } + } + ] + }, + { + "name": "unlock", + "description": "Unlock this Component or a resource of it, such that other Component may use it again. Only the locking Component may unlock.", + "params": [ + { + "name": "resource", + "schema": { + "type": "string" + }, + "required": false + } + ], + "result": { + "name": "result", + "schema": { + "type": "boolean", + "description": "Whether unlocking succeeded or not." + }, + "required": true + }, + "examples": [ + { + "name": "Unock the 'channel1' resource of the Component", + "params": [ + { + "name": "resource", + "value": "channel1" + } + ], + "result": { + "name": "result", + "value": true + } + }, + { + "name": "Unlock Component example", + "params": [ + ], + "result": { + "name": "result", + "value": true + } + } + ] + }, + { + "name": "force_unlock", + "description": "Unock this Component or a resource of it, such that other Component may use it. This may be used even if someone else locked the resource.", + "params": [ + { + "name": "resource", + "schema": { + "type": "string" + }, + "required": false + } + ], + "result": { + "name": "result", + "schema": { + "type": "boolean", + "description": "Whether force unlocking succeeded or not." + }, + "required": true + }, + "examples": [ + { + "name": "Force unlock the 'channel1' resource of the Component", + "params": [ + { + "name": "resource", + "value": "channel1" + } + ], + "result": { + "name": "result", + "value": true + } + }, + { + "name": "Force unlock Component example", + "params": [ + ], + "result": { + "name": "result", + "value": true + } + } + ] + } + ] +} From 5ce9ab6ade6c3c66df613c2b9392308dd6399d72 Mon Sep 17 00:00:00 2001 From: Benedikt Burger <67148916+bmoneke@users.noreply.github.com> Date: Wed, 27 Sep 2023 12:03:43 +0200 Subject: [PATCH 10/15] Change message names in the control protocol according to the RPC message names. Closes #57 --- control_protocol.md | 64 ++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/control_protocol.md b/control_protocol.md index e97d8e3..e7d12d7 100644 --- a/control_protocol.md +++ b/control_protocol.md @@ -48,7 +48,7 @@ For example `N1.CA` is the Full name of the Component `CA` in the Node `N1`. The receiver of a message may be specified by Component name alone if the receiver belongs to the same Node as the sender. In all other cases, the receiver of a message must be specified by the Full name. -The sender of a message must be specified by Full name, except during SIGNIN, when the Component name alone is sufficient. +The sender of a message must be specified by Full name, except for the `sign_in` message, when the Component name alone is sufficient. #### Message composition @@ -91,10 +91,10 @@ In the exchange of messages, only the messages over the wire are shown, the conn ##### Signing-in -After connecting to a Coordinator (`Co1`), a Component (`CA`) shall send a SIGNIN message indicating its Component name. -The Coordinator shall indicate success/acceptance with an ACKNOWLEDGE response, giving the Namespace and other relevant information, or reply with an ERROR, e.g. if the Component name is already taken. +After connecting to a Coordinator (`Co1`), a Component (`CA`) shall send a `sign_in` message (see {ref}`control_protocol.md#coordinator`) indicating its Component name. +The Coordinator shall indicate success/acceptance with a `result` response (according to [JSON-RPC](https://www.jsonrpc.org/specification)), giving the Namespace and other relevant information, or reply with an ERROR, e.g. if the Component name is already taken. In that case, the Coordinator may indicate a suitable, still available variation on the indicated Component name. -The Component may retry SIGNIN with a different chosen name. +The Component may retry signing in with a different chosen name. After a successful handshake, the Coordinator shall store the Component name in its {ref}`control_protocol.md#directory` and shall ensure message delivery to that Component (e.g. by storing the (zmq) connection identity with the local directory). It shall also notify the other Coordinators in the network that this Component signed in, see {ref}`control_protocol.md#coordinator-coordination`. @@ -106,21 +106,21 @@ If a Component does send a message to someone without having signed in, the Coor sequenceDiagram Note over CA,N1: Name "CA" is still free participant N1 as N1.COORDINATOR - CA ->> N1: V|COORDINATOR|CA|H|SIGNIN + CA ->> N1: V|COORDINATOR|CA|H|sign_in Note right of N1: Connection identity "IA" Note right of N1: Stores "CA" with identity "IA" - N1 ->> CA: V|N1.CA|N1.COORDINATOR|H|ACKNOWLEDGE: Namespace is "N1" + N1 ->> CA: V|N1.CA|N1.COORDINATOR|H|result Note left of CA: Stores "N1" as Namespace Note over CA,N1: Name "CA" is already used - CA ->> N1: V|COORDINATOR|CA|H|SIGNIN - N1 ->> CA: V|CA|N1.COORDINATOR|H|ERROR: Name "CA" is already used. + CA ->> N1: V|COORDINATOR|CA|H|sign_in + N1 ->> CA: V|CA|N1.COORDINATOR|H|ERROR: The name is already taken. Note left of CA: May retry with another Name - Note over CA,N1: "CA" has not send SIGNIN + Note over CA,N1: "CA" has not send sign_in Note left of CA: Wants to send a message to CB CA ->> N1: V|N1.CB|CA|H|Content Note right of N1: Does not know CA - N1 ->> CA: V|CA|N1.COORDINATOR|H|ERROR:I do not know you - Note left of CA: Must send a SIGNIN message
before further messaging. + N1 ->> CA: V|CA|N1.COORDINATOR|H|ERROR: Component not signed in yet! + Note left of CA: Must send a sign_in message
before further messaging. ::: @@ -130,7 +130,7 @@ Heartbeats are used to know whether a communication peer is still online. Every message received counts as a heartbeat. -A Component should and a Coordinator shall send a PING and wait some time before considering a connection dead. +A Component should and a Coordinator shall send a `pong` request message (see {ref}`control_protocol.md#actor`) and wait some time before considering a connection dead. A Coordinator shall follow the {ref}`control_protocol.md#signing-out` for a signed in Component considered dead. :::{note} @@ -140,18 +140,18 @@ TBD: Heartbeat details are still to be determined. ##### Signing out -A Component should send a SIGNOUT message to its Coordinator when it stops participating in the Network. -The Coordinator shall ACKNOWLEDGE the sign-out and remove the Component name from its local {ref}`control_protocol.md#directory`. +A Component should send a `sign_out` message (see {ref}`control_protocol.md#coordinator`) to its Coordinator when it stops participating in the Network. +The Coordinator shall acknowledge the sign-out with a `result` message and remove the Component name from its local {ref}`control_protocol.md#directory`. It shall also notify the other Coordinators in the network that this Component signed out, see {ref}`control_protocol.md#coordinator-coordination`. :::{mermaid} sequenceDiagram - CA ->> N1: V|COORDINATOR|N1.CA|H|SIGNOUT + CA ->> N1: V|COORDINATOR|N1.CA|H|sign_out participant N1 as N1.COORDINATOR - N1 ->> CA: V|N1.CA|N1.COORDINATOR|H|ACKNOWLEDGE + N1 ->> CA: V|N1.CA|N1.COORDINATOR|H|result Note right of N1: Removes "CA" with identity "IA"
from local Directory Note right of N1: Notifies other Coordinators about sign-out of "CA" - Note left of CA: Shall not send any message anymore except SIGNIN + Note left of CA: Shall not send any message anymore except sign_in ::: @@ -215,9 +215,9 @@ flowchart TB CnS-->|yes| Clocal{CA in
local Directory?} Clocal -->|yes| CidKnown{iA is CA's identity?} CidKnown -->|yes| RemIdent - Clocal -.->|no| E1[ERROR: Sender unknown] ==>|"iA|V|nS.CA|N1.COORDINATOR|H|ERROR: Sender unknown"| S + Clocal -.->|no| E1[ERROR: Component not signed in yet!] ==>|"iA|V|nS.CA|N1.COORDINATOR|H|ERROR: Component not signed in yet!"| S S[send] ==> WA([N1.CA DEALER]) - CidKnown -.->|no| E2[ERROR: Name and identity do not match]==>|"iA|V|nS.CA|N1.COORDINATOR|H|ERROR: Name and identity do not match"| S + CidKnown -.->|no| E2[ERROR: Component not signed in yet!]==>|"iA|V|nS.CA|N1.COORDINATOR|H|ERROR: Component not signed in yet!"| S RemIdent[remove sender identity] == "V|nR.recipient|nS.CA|H|Content" ==> CnR CnR -- "is None" --> Local CnR{nR?} -- "== N1"--> Local @@ -227,7 +227,7 @@ flowchart TB Local2a -->|yes, with Identity iB| Local2 Local2[add recipient identity iB] == "iB|V|nR.recipient|nS.CA|H|Content" ==> R1[send] R1 == "V|nR.recipient|nS.CA|H|Content" ==> W1([Wire to N1.recipient DEALER]) - Local2a -.->|no| E3[ERROR recipient unknown
send Error to original sender] ==>|"V|nS.CA|N1.COORDINATOR|H|ERROR N1.recipient is unknown"|CnR + Local2a -.->|no| E3[ERROR: Receiver is not in addresses list
send Error to original sender] ==>|"V|nS.CA|N1.COORDINATOR|H|
ERROR: N1.recipient is unknown"|CnR CnR -- "== N2" --> Keep Keep[send to N2.COORDINATOR] == "V|nR.recipient|nS.CA|H|Content" ==> R2[send] R2 == "V|nR.recipient|nS.CA|H|Content" ==> W2([Wire to N2.COORDINATOR ROUTER]) @@ -238,7 +238,7 @@ flowchart TB R1 S end - subgraph Co1 DEALER socket
to N2.COORDINATOR + subgraph "Co1 DEALER socket
to N2.COORDINATOR" R2 end ::: @@ -275,27 +275,27 @@ sequenceDiagram activate d1 Note left of d1: created with
name "temp-NS" d1-->>r2: connect to address2 - d1->>r2: V|COORDINATOR|N1.COORDINATOR|H|
CO_SIGNIN + d1->>r2: V|COORDINATOR|N1.COORDINATOR|H|
coordinator_sign_in Note right of r2: stores N1 identity - r2->>d1: V|N1.COORDINATOR|N2.COORDINATOR|H|ACK + r2->>d1: V|N1.COORDINATOR|N2.COORDINATOR|H|result Note left of d1: DEALER name
set to "N2" - d1->>r2: V|N1.COORDINATOR|N2.COORDINATOR|H|
Here is my local directory
and Coordinator addresses + d1->>r2: V|N1.COORDINATOR|N2.COORDINATOR|H|
add_nodes(Coordinator addresses)
record_components Note right of r2: Updates global
Directory and signs
in to all unknown
Coordinators,
also N1 Note over d1,r2: Mirror of above sign-in procedure activate d2 Note left of d2: created with
name "N1" d2-->>r1: connect to address1 - d2->>r1: V|COORDINATOR|N2.COORDINATOR|H|
CO_SIGNIN + d2->>r1: V|COORDINATOR|N2.COORDINATOR|H|
coordinator_sign_in Note right of r1: stores N2 identity - r1->>d2: V|N2.COORDINATOR|N1.COORDINATOR|H|ACK + r1->>d2: V|N2.COORDINATOR|N1.COORDINATOR|H|result Note left of d2: Name is already "N1" - d2->>r1: V|N2.COORDINATOR|N1.COORDINATOR|H|
Here is my local directory
and Coordinator addresses + d2->>r1: V|N2.COORDINATOR|N1.COORDINATOR|H|
add_nodes(Coordinator addresses)
record_components Note right of r1: Updates global
Directory and signs
in to all unknown
Coordinators Note over r1,d2: Sign out between two Coordinators Note right of r1: shall sign out from N2 - d1->>r2: CO_SIGNOUT + d1->>r2: coordinator_sign_out Note right of r2: removes N1 identity - d2->>-r1: CO_SIGNOUT + d2->>-r1: coordinator_sign_out Note right of r1: removes N2 identity deactivate d1 ::: @@ -311,6 +311,10 @@ Each Coordinator shall keep an up-to-date global {ref}`control_protocol.md#direc For this, whenever a Component signs in to or out from its Coordinator, the Coordinator shall notify all the other Coordinators regarding this event. The other Coordinators shall update their global Directory according to this message (add or remove an entry). +:::{note} +TBD: These updates have to be determined. +::: + On request, Coordinators shall send the Names of their local or global Directory, depending on the request type. For the format of the Messages, see {ref}`control_protocol.md#message-layer`. @@ -396,7 +400,7 @@ An {ref}`control_protocol.md#Actor` which support locking resources MUST offer t :file: schemas/locking_actor.json ::: -Accessing a locked resource (the whole Component or parts of it) or trying to unlock one, locked by another Component, will raise {ref}`control_protocol.md#locking_errors`. +Accessing a locked resource (the whole Component or parts of it) or trying to unlock one, locked by another Component, will raise {ref}`control_protocol.md#errors`. ### Errors From 3bcbfcda868096ed7b2e1b2a11602fc9b16b0097 Mon Sep 17 00:00:00 2001 From: Benedikt Burger <67148916+bmoneke@users.noreply.github.com> Date: Wed, 27 Sep 2023 12:08:21 +0200 Subject: [PATCH 11/15] Make data-view happy (removing examples). --- control_protocol.md | 2 +- schemas/locking_actor.json | 78 ++------------------------------------ 2 files changed, 4 insertions(+), 76 deletions(-) diff --git a/control_protocol.md b/control_protocol.md index e7d12d7..618802e 100644 --- a/control_protocol.md +++ b/control_protocol.md @@ -400,7 +400,7 @@ An {ref}`control_protocol.md#Actor` which support locking resources MUST offer t :file: schemas/locking_actor.json ::: -Accessing a locked resource (the whole Component or parts of it) or trying to unlock one, locked by another Component, will raise {ref}`control_protocol.md#errors`. +Accessing a locked resource (the whole Component or parts of it) or trying to unlock one, locked by another Component, will raise appropriate {ref}`control_protocol.md#errors`. ### Errors diff --git a/schemas/locking_actor.json b/schemas/locking_actor.json index 46e8259..e1e7b11 100644 --- a/schemas/locking_actor.json +++ b/schemas/locking_actor.json @@ -24,31 +24,7 @@ "description": "Whether locking succeeded or not." }, "required": true - }, - "examples": [ - { - "name": "Lock the 'channel1' resource of the Component", - "params": [ - { - "name": "resource", - "value": "channel1" - } - ], - "result": { - "name": "result", - "value": true - } - }, - { - "name": "Lock Component example", - "params": [ - ], - "result": { - "name": "result", - "value": true - } - } - ] + } }, { "name": "unlock", @@ -69,31 +45,7 @@ "description": "Whether unlocking succeeded or not." }, "required": true - }, - "examples": [ - { - "name": "Unock the 'channel1' resource of the Component", - "params": [ - { - "name": "resource", - "value": "channel1" - } - ], - "result": { - "name": "result", - "value": true - } - }, - { - "name": "Unlock Component example", - "params": [ - ], - "result": { - "name": "result", - "value": true - } - } - ] + } }, { "name": "force_unlock", @@ -114,31 +66,7 @@ "description": "Whether force unlocking succeeded or not." }, "required": true - }, - "examples": [ - { - "name": "Force unlock the 'channel1' resource of the Component", - "params": [ - { - "name": "resource", - "value": "channel1" - } - ], - "result": { - "name": "result", - "value": true - } - }, - { - "name": "Force unlock Component example", - "params": [ - ], - "result": { - "name": "result", - "value": true - } - } - ] + } } ] } From a744a4e7b01fab73dc05ab877dce4eda52da0eb7 Mon Sep 17 00:00:00 2001 From: Benedikt Burger <67148916+bmoneke@users.noreply.github.com> Date: Wed, 11 Oct 2023 10:37:23 +0200 Subject: [PATCH 12/15] Change logging levels to strings. --- schemas/component_optional.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/schemas/component_optional.json b/schemas/component_optional.json index 1582365..bfac673 100644 --- a/schemas/component_optional.json +++ b/schemas/component_optional.json @@ -11,11 +11,10 @@ { "name": "level", "schema": { - "type": "integer", - "multipleOf": 10, - "minimum": 0, - "maximum": 100 + "type": "string", + "enum": ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] }, + "description": "The logging level, for a description of the different levels see https://docs.python.org/3/library/logging.html#logging-levels.", "required": true } ], From b4de36cc63af8789af999616f8270032866cf2fb Mon Sep 17 00:00:00 2001 From: Benedikt Burger <67148916+bmoneke@users.noreply.github.com> Date: Wed, 11 Oct 2023 10:45:11 +0200 Subject: [PATCH 13/15] Fix environment.yml. --- environment.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 0296c9c..5499b1c 100644 --- a/environment.yml +++ b/environment.yml @@ -4,7 +4,8 @@ channels: dependencies: - myst-parser=0.18.1 - python=3.11 + - pip - sphinx=5.3.0 - sphinxcontrib-mermaid=0.8.1 - sphinx_rtd_theme=1.1.1 - - sphinx_data_viewer=0.1.2 + - sphinx-data-viewer=0.1.2 From 12d4589a42de491c1fc5577debf797b25c2657e9 Mon Sep 17 00:00:00 2001 From: Benedikt Burger <67148916+bmoneke@users.noreply.github.com> Date: Wed, 1 Nov 2023 12:08:43 +0100 Subject: [PATCH 14/15] Improve actor.json according to current Actor/Device definitions --- schemas/actor.json | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/schemas/actor.json b/schemas/actor.json index 39aa4cf..33c92d7 100644 --- a/schemas/actor.json +++ b/schemas/actor.json @@ -7,10 +7,11 @@ "methods": [ { "name": "get_parameters", - "description": "Get the values for parameters.", + "description": "Get values of Device parameters.", "params": [ { "name": "parameters", + "description": "List of parameter names to get the values of.", "schema": { "type": "array", "items": { @@ -50,12 +51,13 @@ }, { "name": "set_parameters", + "description": "Set values of Device parameters.", "params": [ { "name": "parameters", + "description": "Object with parameter-value pairs", "schema": { "type": "object", - "description": "Object with parameter-value pairs", "additionalProperties": true }, @@ -91,10 +93,11 @@ }, { "name": "call_action", - "description": "Call an Action of this Component. All other arguments are handed to the action.", + "description": "Call an Action of the Device.", "params": [ { "name": "action", + "description": "Name of the action to call.", "schema": { "type": "string" }, @@ -102,9 +105,9 @@ }, { "name": "args", + "description": "List of positional arguments.", "schema": { "type": "array", - "description": "List of positional arguments.", "items": { "$ref": "#/components/any" } @@ -122,25 +125,21 @@ }, "examples": [ { - "name": "Calling some action with keyword arguments only.", + "name": "Calling some action 'action'.", "params": [ { "name": "action", - "value": "super_action" + "value": "action" }, { "name": "args", - "value": null + "value": [5, 7] - }, - { - "name": "argument1", - "value": 5 } ], "result": { "name": "result", - "value": "result of 'super_action(argument1=5)'." + "value": "result of `action(5, 7)`" } } ] From 95c2c696fad81ada57b665383a935806988e1c7a Mon Sep 17 00:00:00 2001 From: Benedikt Burger <67148916+BenediktBurger@users.noreply.github.com> Date: Wed, 20 Dec 2023 17:11:48 +0100 Subject: [PATCH 15/15] Fix typo. --- schemas/coordinator.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/coordinator.json b/schemas/coordinator.json index 458927a..55f4015 100644 --- a/schemas/coordinator.json +++ b/schemas/coordinator.json @@ -206,7 +206,7 @@ ] }, { - "name": "remove_expired_adresses", + "name": "remove_expired_addresses", "description": "Clean all expired addresses from the directory.", "params": [ {