Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Control protocol transport layer. #38

Merged
merged 29 commits into from
Feb 23, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
238b2df
Initial version of the Control protocol transport layer.
BenediktBurger Feb 2, 2023
2f14be0
Coordinator has ID COORDINATOR.
BenediktBurger Feb 3, 2023
df1a074
Changing (DIS)CONNECT to SIGNIN/OUT.
BenediktBurger Feb 3, 2023
fac5587
Decision flow in Coordinator expanded (including errors)
BenediktBurger Feb 3, 2023
33cfb3b
Merge branch 'main' of [email protected]:pymeasure/leco-protocol.git int…
BenediktBurger Feb 3, 2023
bd59eaa
Wording and links changed to accomodate Nodes.
BenediktBurger Feb 3, 2023
5b1b885
Make ROUTER socket more understantable. And small diagram changes.
BenediktBurger Feb 3, 2023
588e35a
Decision diagram improved.
BenediktBurger Feb 6, 2023
e711d6d
Changing Coordinator names to Node names
BenediktBurger Feb 6, 2023
fff85be
Change from ID to name. Also address book becomes directory.
BenediktBurger Feb 7, 2023
553b37f
Version and Header added to message flow.
BenediktBurger Feb 7, 2023
daee254
Graph changed to variables only.
BenediktBurger Feb 7, 2023
705aa8a
Coordinator-Coordinator sign in/out added.
BenediktBurger Feb 8, 2023
b3226d2
Coordinator name set to COORDINATOR (ASCII).
BenediktBurger Feb 8, 2023
d4cd17f
Language improvements by bilderbuchi
BenediktBurger Feb 9, 2023
d938423
Naming changed and ZMQ information moved to other files.
BenediktBurger Feb 9, 2023
0dbb7cf
Language improvements by bklebel
BenediktBurger Feb 10, 2023
90208de
Information regarding connected Components is stored in the local or …
BenediktBurger Feb 10, 2023
6af9c0b
Router socket better explained.
BenediktBurger Feb 10, 2023
0c69314
Coordinator sign-in improved.
BenediktBurger Feb 13, 2023
c748a59
Wordings improved.
BenediktBurger Feb 13, 2023
c62844c
Apply suggestions from @bklebel
BenediktBurger Feb 14, 2023
1c5ee09
Small clarity improvements.
BenediktBurger Feb 13, 2023
509d57f
Sign-in procedure improved.
BenediktBurger Feb 14, 2023
d26cbee
Merge branch 'main' into control-transport-layer
bilderbuchi Feb 14, 2023
7ecbc43
Increase depth of generated level heading anchors.
bilderbuchi Feb 14, 2023
9fc3316
Small changes and links fixed (auto-slugs to not work sometimes).
BenediktBurger Feb 15, 2023
217dcef
Links made to work and Directory overhaul.
BenediktBurger Feb 16, 2023
5929811
Restrict Component name and Namespace to printable ASCII, closes #47
BenediktBurger Feb 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions control_protocol.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
# Control protocol

The control protocol transmits messages via its {ref}`transport-layer` from one Component to another one.
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved
The {ref]}`message-layer` is the common language to understand commands, thus creating a remote procedure call.


(transport-layer)=
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved
## Transport layer

The transport layer ensures, that a message arrives its destination.
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved


### Sockets and Connections

We use [zmq](https://zeromq.org/) sockets for our communication. For more details see the [zmq guide](https://zguide.zeromq.org/) or [zmq API](http://api.zeromq.org/)
bilderbuchi marked this conversation as resolved.
Show resolved Hide resolved
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved


#### Definitions

Zmq messages consist in a series of frames, each is a byte sequence.
We indicate the separation between frames with `|`.
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved
An empty frame is indicated with two frame separators `||`, even at the beginning or end of a message.
For example `||Second frame|Third frame||Fifth frame` consists of 5 frames.
The first and fourth frames are empty frames.
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved


#### Configuration

Each {ref}`Coordinator <components.md#coordinator>` shall offer one ROUTER socket, bound to a host name (or IP address or any address of a computer with "*") and port in the DMT.

{ref}`Components <components.md#components>` shall have one DEALER socket connecting to one Coordinator's ROUTER socket.

Coordinators shall have one DEALER socket per other Coordinator in the network.
This DEALER socket shall connect to the other Coordinator's ROUTER socket.

Messages must be sent to a Coordinator's ROUTER socket.
bklebel marked this conversation as resolved.
Show resolved Hide resolved


#### Particularities of ROUTER sockets

A small introduction to ROUTER sockets, for more details see [zmq guide chapter 3](https://zguide.zeromq.org/docs/chapter3/#Exploring-ROUTER-Sockets).
bilderbuchi marked this conversation as resolved.
Show resolved Hide resolved

A router socket assigns a random identity to each connecting peer.
If, for example, two Components `CA`, `CB` connect to the ROUTER sockets, the socket assigns the identities, which we call `IA`, `IB` (they can be any bytes sequence).
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved

Whenever a message is sent to the ROUTER socket from a peer, it reads as a first frame that identity.
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved
For example if `CA` sends the message `Request A`, the ROUTER socket will read `IA|Request A`.
That way, you can return an answer to exactly the original peer and not, for example `CB`.
If you call the ROUTER's send command with `IA|Reply A`, the socket will send `Reply A` to the peer, whose connection is `IA`, in this case `CA`.
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved

The following diagram shows this example communication with two Components:
:::{mermaid}
graph TD
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved
CA([CA]) -->|"Request A"| D[recv] -->|"IA|Request A"| A([Coordinator])
CB([CB]) -->|"Request B"| C[recv] -->|"IB|Request B"| A
subgraph ROUTER socket
B
C
D
E
end
A -. "IA|Reply A" .-> B
B[send] -. "Reply A" .-> CA
A -. "IB|Reply B" .-> E
E[send] -. "Reply B" .-> CB
:::


### Naming scheme

Each Component has an individual ID, given by the user, the Component ID.
A Component ID must be a series of bytes, without the ASCII character "." (byte value 46).
Component IDs must be unique in a Node, i.e. among the Componets connected to a single Coordinator.

As each Component belongs to exactly one Coordinator, it is fully identified by the combination of Node ID and Component ID.
This _full ID_ is the composition of Node ID, ".", and Component ID.

The receiver of a message may be specified without the Node ID, if the receiver lives in the same Node.

:::{note}
How to address the Coordinator? I used "no Recipient".
Or a fixed name?
:::
bklebel marked this conversation as resolved.
Show resolved Hide resolved


### Message composition

A message consists in two or more frames.
1. The receiver full ID.
2. The sender full ID.
3. Message content: The optional payload, which can be 0 or more frames.


### Conversation protocol

In the protocol examples, `CA`, `CB`, etc. indicate Components.
`Co1`, `Co2`, etc. indicate Coordinators.

Here the Message content is expressed in plain English, for the exact definition see {ref}`message-layer`.

In the exchange of messages, only the messages over the wire are shown, the connection identity of the ROUTER socket is not shown.
bilderbuchi marked this conversation as resolved.
Show resolved Hide resolved


#### Communication with the Coordinator

##### Initial connection

After connecting to a Coordinator (`Co1`), a Component (`CA`) shall send a CONNECT message indicating its ID.
The Coordinator shall respond with an ACKNOWLEDGE, giving the Node ID and other relevant information, or with an ERROR, if the ID is already taken.
After that successful handshake, the Coordinator shall store the connection identity and correspondind Component ID in its address book.
:::{note}
publish that information
:::
Similarly, the Component shall store the Node ID and use it from this moment to generate its full ID.

:::{mermaid}
sequenceDiagram
Note over CA,Co1: ID "CA" is still free
CA ->> Co1: ||CA|CONNECT
Note right of Co1: Connection identity "IA"
Note right of Co1: Stores "CA" with identity "IA"
Co1 ->> CA: Co1.CA||ACKNOWLEDGE: Node ID is "Co1"
Note left of CA: Stores "Co1" as Node ID
Note over CA,Co1: ID "CA" is already used
CA ->> Co1: ||CA|CONNECT
Co1 ->> CA: CA||ERROR: ID "CA" is already used.
Note left of CA: May retry with another ID
:::


##### Heartbeat

We use heartbeat to know, whether a communication partner is still online.
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved

Every message received counts as a heartbeat.

:::{note}
TBD: Respond to every non empty message with an empty one?
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved
:::


##### Disconnecting

A Component should tell a Coordinator, when it stops working, with a DISCONNECT message.
The Coordinator shall ACKNOWLEDGE the disconnect and remove the ID from its address book.
:::{note}
publish that information
:::

:::{mermaid}
sequenceDiagram
CA ->> Co1: ||Co1.CA|DISCONNECT
Co1 ->> CA: Co1.CA||ACKNOWLEDGE
Note right of Co1: Removes "CA" with identity "IA"<br> from address book
Note left of CA: Shall not send any message anymore
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved
:::


#### Communication with other Components

The following two examples show, how a message is transferred between two components `CA`, `CB` via one or two Coordinators.
Coordinators shall send messages from their DEALER socket to other Coordinator's ROUTER socket.
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved

Coordinators shall hand on the message to the corresponding Coordinator or connected Component.
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved


:::{mermaid}
sequenceDiagram
CA ->> Co1: Co1.CB|Co1.CA| Give me property A.
Co1 ->> CB: Co1.CB|Co1.CA| Give me property A.
Note left of CB: Reads property A
CB ->> Co1: Co1.CA|Co1.CB| Property A has value 5.
Co1 ->> CA: Co1.CA|Co1.CB| Property A has value 5.
Note over CA,Co1: The first message could be as well:
CA ->> Co1: CB|Co1.CA| Give me property A.
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved
:::


:::{mermaid}
sequenceDiagram
CA ->> Co1: Co2.CB|Co1.CA| Give me property A.
Note over Co1,Co2: Co1.DEALER socket sends to Co2.ROUTER
Co1 ->> Co2: Co2.CB|Co1.CA| Give me property A.
Co2 ->> CB: Co2.CB|Co1.CA| Give me property A.
Note left of CB: Reads property A
CB ->> Co2: Co1.CA|Co2.CB| Property A has value 5.
Note over Co1,Co2: Co2.DEALER socket sends to Co1.ROUTER
Co2 ->> Co1: Co1.CA|Co2.CB| Property A has value 5.
Co1 ->> CA: Co1.CA|Co2.CB| Property A has value 5.
:::


The following flow chart shows the decision scheme and message modification in a Coordinator.
`IA` and `IB` are the connection identities of `CA` and `Co1.Recipient`.
Bold arrows indicate message flow, thin lines indicate decision flow.

:::{mermaid}
flowchart TB
bilderbuchi marked this conversation as resolved.
Show resolved Hide resolved
C([CA DEALER]) == "NodeID.Recipient|Co1.CA|Content" ==> R0
R0[Co1 ROUTER receive] == "IA|NodeID.Recipient|Co1.CA|Content" ==> Code[remove sender identity]
Code == "NodeID.Recipient|Co1.CA|Content" ==> NS
NS -- "is None" --> Local
NS{NodeID} -- "== Co1"--> Local
Local{Recipient<br> is None} -- "yes" --> Self([Message for Co1<br> itself])
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved
Local -- "no" --> Local2
Local2[add Recipient identity IB] == "IB|NodeID.Recipient|Co1.CA|Content" ==> R1[send]
R1 == "NodeID.Recipient|Co1.CA|Content" ==> W1([Wire to Co1.Recipient DEALER])
NS -- "== connected Coordinator Co2" --> Keep
Keep[send to Coordinator Co2] == "NodeID.Recipient|Co1.CA|Content" ==> R2[send]
R2 == "NodeID.Recipient|Co1.CA|Content" ==> W2([Wire to Co2 ROUTER])
NS -- "reachable via Coordinator CoX" --> Augment
Augment[send via CoX] == "NodeID.Recipient|Co1.CA|Content" ==> R3[send]
R3 == "NodeID.Recipient|Co1.CA|Content"==> W3([Wire to CoX ROUTER])
subgraph Co1 ROUTER socket
R1
end
subgraph Co1 DEALER socket to Co2
R2
end
subgraph Co1 DEALER socket to CoX
R3
end
:::



(message-layer)=
## Message layer

:::{note}
TODO
:::
1 change: 1 addition & 0 deletions index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
introduction
goals
components
control_protocol
glossary
Hello_world

Expand Down