.. SPDX-FileCopyrightText: Huawei Inc. .. .. SPDX-License-Identifier: CC-BY-4.0 Vending Machine Blueprint Applications Interface and Protocol ############################################################# .. contents:: :depth: 4 Communication Protocol ********************** The "Vending Machine" blueprint will take advantage of two applications: a UI and an "IO Controller". These applications will exchange messages over a defined interface using a specific protocol. For the scope of this specification, the communication will happen over plain WebSockets/TCP. Specification ------------- In terms of roles, we have a client and a server. The "IO Controller" acts as a server while the "UI" process, as a client. As a minimum client/server specification, the applications will exchange messages as per the following diagram: .. code-block:: ┌────────────────┐ selection ┌─────────────────────┐ │ ├───────────────────>┤ │ │ │ │ │ │ │ deliver │ IO │ │ UI Application ├───────────────────>┤ Control Application │ │ (client) │ │ (server) │ │ │ delivered │ │ │ │◄───────────────────┤ │ └────────────────┘ └─────────────────────┘ Static information can be set in configuration files shared between both applications (e.g. for items name, a timeout for simulated actions, number of item slots, etc.). Server application is made on generic concepts inspired by WoT/WebThings: - properties: set a *selection* of products - selection is a fixed size array and items are identified from indices in this array while the values represent the associated item quantity - actions: request a *deliver* order - order also contain the current selection as a parameter - events: *delivered* event will notify that the *deliver* action was finished - event is delivered based on the *addEventSubscription* subscription message Thoses objects will be used through websockets's messages on default endpoint (ie: `<ws://localhost:8888/>`). Client request's payloads are formatted using JSON structures. Below there is an example for each of the types defined: Properties ========== .. code-block:: json { "messageType": "setProperty", "data": { "selection": [0, 0, 0, 1] } } Actions ======= .. code-block:: json { "messageType": "requestAction", "data": { "deliver": { "input": { "selection": [0, 1, 0, 0] } } } } Events ====== The client needs to send a subscription message once and listen from server's event messages: .. code-block:: json { "messageType": "addEventSubscription", "data": { "delivered": {} } } .. code-block:: json { "messageType": "event", "data": { "delivered": {} } } Inter-application message flow ------------------------------ The UI and Control applications will adhere to the message schema defined above. The message flow is described as it follows: .. code-block:: ┌────┐ ┌────┐ │ │ │ │ │ │ │ │ │ │ selection │ │ │ ├─────────────────────►│ │ │ │ │ │ │ │ selection │ │ │ ├─────────────────────►│ │ │ │ │ │ │ │ selection │ │ │ ├─────────────────────►│ │ │ │ │ │ │ │ │ │ │ │ [...] │ │ │ │ │ │ │ UI │ [...] │ IO │ │ │ │ │ │ │ │ │ │ ├─────────────────────►│ │ │ │ │ │ │ │ │ │ │ │ deliver │ │ │ ├─────────────────────►│ │ │ │ │ │ │ │ delivered │ │ │ │◄─────────────────────┤ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └────┘ └────┘ Detailed example flow: Firstly, client is intializing by subscribing for server's future **"delivered" events**: .. code-block:: json { "messageType": "addEventSubscription", "data": { "delivered": {} } } Client's application is setting an empty selection on server and then UI will wait for user inputs: .. code-block:: json { "messageType": "setProperty", "data": { "selection": [0, 0, 0, 0] } } User selects one product (one of type "1"): - UI will be updated accordigly - The client process makes a request to the server to set **selection "property"** .. code-block:: json { "messageType": "setProperty", "data": { "selection": [0, 1, 0, 0] } } The IO Controller will turn on the associated LEDs to show another visual indication. Then the user decides to add 1 more product of type "3": .. code-block:: json { "messageType": "setProperty", "data": { "selection": [0, 1, 0, 1] } } The user confirms the order by pressing the relevant UI element, then a **"deliver" action"** is sent from client to the server: .. code-block:: json { "messageType": "requestAction", "data": { "deliver": { "input": { "selection": [0, 1, 0, 1] } } } } The UI application will be blocked until ready or timeout is reached: - watchdog/timeout timer starts on UI/client - UI waits for the **delivered** event Processing is done server-side and **delivered event** is triggered: .. code-block:: json { "messageType": "event", "data": { "delivered": {} } } The UI is unblocked and ready for new selection (it should reinitialized to empty). If no "delivered" event after a defined timeout, the UI will display an "out of order" message and show a "reset" button to refresh for the next order. Software Dependencies Versions ------------------------------ Oniro Project supports the following libraries for message encoding/decoding/parsing and the communication protocol: * `libwebsockets <https://libwebsockets.org/>`_ 4.0.1 * `cjson <https://github.com/DaveGamble/cJSON/>`_ 1.7.13 (to be upgraded to 1.7.14 for OpenHarmony convergence) * `json-c <https://github.com/json-c/json-c>`_ 0.13.1 Extra software could be integrated if needed: * `libmicrohttpd <https://git.gnunet.org/libmicrohttpd.git/tree/src/include/microhttpd.h>`_ For prototyping purposes server can be easily implemented using `webthings framework <https://webthings.io/>`_. Message schema -------------- Selection Message Schema ======================== The schema for the "selection" messages is: .. code-block:: json { "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://example.com/example.json", "type": "object", "title": "The root schema", "description": "The root schema comprises the entire JSON document.", "default": {}, "examples": [ { "messageType": "setProperty", "data": { "selection": [ 0, 1, 0, 0 ] } } ], "required": [ "messageType", "data" ], "properties": { "messageType": { "$id": "#/properties/messageType", "type": "string", "title": "The messageType schema", "default": "", "examples": [ "setProperty" ] }, "data": { "$id": "#/properties/data", "type": "object", "title": "The data schema", "default": {}, "examples": [ { "selection": [ 0, 1, 0, 0 ] } ], "required": [ "selection" ], "properties": { "selection": { "$id": "#/properties/data/properties/selection", "type": "array", "title": "The selection schema", "default": [], "examples": [ [ 0, 1 ] ], "additionalItems": true, "items": { "$id": "#/properties/data/properties/selection/items", "anyOf": [ { "$id": "#/properties/data/properties/selection/items/anyOf/0", "type": "integer", "title": "The first anyOf schema", "default": 0, "examples": [ 0, 1 ] } ] } } }, "additionalProperties": true } }, "additionalProperties": true } Deliver Message Schema ====================== The schema for the "deliver" messages is: .. code-block:: json { "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://example.com/example.json", "type": "object", "title": "The root schema", "description": "The root schema comprises the entire JSON document.", "default": {}, "examples": [ { "messageType": "requestAction", "data": { "deliver": { "input": { "selection": [ 0, 1, 0, 0 ] } } } } ], "required": [ "messageType", "data" ], "properties": { "messageType": { "$id": "#/properties/messageType", "type": "string", "title": "The messageType schema", "default": "", "examples": [ "requestAction" ] }, "data": { "$id": "#/properties/data", "type": "object", "title": "The data schema", "default": {}, "examples": [ { "deliver": { "input": { "selection": [ 0, 1, 0, 0 ] } } } ], "required": [ "deliver" ], "properties": { "deliver": { "$id": "#/properties/data/properties/deliver", "type": "object", "title": "The deliver schema", "default": {}, "examples": [ { "input": { "selection": [ 0, 1, 0, 0 ] } } ], "required": [ "input" ], "properties": { "input": { "$id": "#/properties/data/properties/deliver/properties/input", "type": "object", "title": "The input schema", "default": {}, "examples": [ { "selection": [ 0, 1, 0, 0 ] } ], "required": [ "selection" ], "properties": { "selection": { "$id": "#/properties/data/properties/deliver/properties/input/properties/selection", "type": "array", "title": "The selection schema", "default": [], "examples": [ [ 0, 1 ] ], "additionalItems": true, "items": { "$id": "#/properties/data/properties/deliver/properties/input/properties/selection/items", "anyOf": [ { "$id": "#/properties/data/properties/deliver/properties/input/properties/selection/items/anyOf/0", "type": "integer", "title": "The first anyOf schema", "default": 0, "examples": [ 0, 1 ] } ] } } }, "additionalProperties": true } }, "additionalProperties": true } }, "additionalProperties": true } }, "additionalProperties": true } Delivered Message Schema ======================== The schema for the "delivered" messages is: .. code-block:: json { "$schema": "http://json-schema.org/draft-07/schema", "type": "object", "title": "The root schema", "description": "The root schema comprises the entire JSON document.", "default": {}, "examples": [ { "messageType": "event", "data": { "delivered": {} } } ], "required": [ "messageType", "data" ], "properties": { "messageType": { "$id": "#/properties/messageType", "type": "string", "title": "The messageType schema", "description": "An explanation about the purpose of this instance.", "default": "", "examples": [ "event" ] }, "data": { "$id": "#/properties/data", "type": "object", "title": "The data schema", "description": "An explanation about the purpose of this instance.", "default": {}, "examples": [ { "delivered": {} } ], "required": [ "delivered" ], "properties": { "delivered": { "$id": "#/properties/data/properties/delivered", "type": "object", "title": "The delivered schema", "description": "An explanation about the purpose of this instance.", "default": {}, "examples": [ {} ], "required": [], "additionalProperties": true } }, "additionalProperties": true } }, "additionalProperties": true } Previous event will be notified if the client sends a subscription message: .. code-block:: json { "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://example.com/example.json", "type": "object", "title": "The root schema", "description": "The root schema comprises the entire JSON document.", "default": {}, "examples": [ { "messageType": "addEventSubscription", "data": { "delivered": {} } } ], "required": [ "messageType", "data" ], "properties": { "messageType": { "$id": "#/properties/messageType", "type": "string", "title": "The messageType schema", "description": "An explanation about the purpose of this instance.", "default": "", "examples": [ "addEventSubscription" ] }, "data": { "$id": "#/properties/data", "type": "object", "title": "The data schema", "description": "An explanation about the purpose of this instance.", "default": {}, "examples": [ { "delivered": {} } ], "required": [ "delivered" ], "properties": { "delivered": { "$id": "#/properties/data/properties/delivered", "type": "object", "title": "The delivered schema", "description": "An explanation about the purpose of this instance.", "default": {}, "examples": [ {} ], "required": [], "additionalProperties": true } }, "additionalProperties": true } }, "additionalProperties": true } Current assumptions ------------------- * Both of the applications (server/client, "UI"/"IO Controller" are running on the same, Linux-based target. * The quantity of a selection is maximum "1". This means that the selection array can contain values of 0 or 1. * The availability from the perspective of the "IO Controller" is infinite.