|
| 1 | +# SOME/IP Network Daemon Design Proposal |
| 2 | + |
| 3 | +## Intro |
| 4 | + |
| 5 | +Because of safety considerations we have a specific `SOME/IP network daemon`, which is responsible to handle the SOME/IP |
| 6 | +related network communication. |
| 7 | +It will be based on the open source SOME/IP implementation `vSomeIP`, which is provided [here](https://github.com/COVESA/vsomeip/blob/master/README.md). |
| 8 | + |
| 9 | +Within S-CORE architecture, there will be different upper layers, which all need to use this network daemon. Currently |
| 10 | +under discussion: |
| 11 | +- Generic SOME/IP Gateway Daemon |
| 12 | +- OEM specific SOME/IP Gateway Daemon |
| 13 | +- S-CORE application using `score::mw::com` with a specific SOME/IP binding |
| 14 | + |
| 15 | +The interfacing with the `SOME/IP network daemon` shall be identical for all upper layers. |
| 16 | +In this proposal we define a common architecture, which should end up in re-usable building blocks/libs, which are then |
| 17 | +used by all upper layers uniformly. |
| 18 | + |
| 19 | +## Basic Architecture |
| 20 | + |
| 21 | +There exist two "channels" between the `SOME/IP network daemon` and an upper layer using/relying on it: |
| 22 | + |
| 23 | +- a DATA channel |
| 24 | +- a CONTROL channel |
| 25 | + |
| 26 | +we explicitly separate these two channels as we intend to implement them with different technical mechanisms. |
| 27 | + |
| 28 | + |
| 29 | + |
| 30 | +### DATA channel |
| 31 | + |
| 32 | +The DATA channel transports the (SOME/IP) service instance related data. This is: |
| 33 | + |
| 34 | +- Event payloads |
| 35 | +- Field payloads |
| 36 | +- Method payloads (in-arguments and return values) |
| 37 | + |
| 38 | +We intend to use shared-memory to implement the DATA channel. Mainly because payloads listed above might be large and |
| 39 | +using shared-memory as exchange mechanism might save copies in certain cases, which helps with performance. |
| 40 | +Additionally: Since there are already existing building blocks for shared-memory communication either in the more high |
| 41 | +level form of the `score::mw::com` SHM/LoLa binding or in its underlying `lib/memory/shared`. |
| 42 | + |
| 43 | +Initially we plan implement this by DATA channel via creation of corresponding `score::mw::com` skeletons and proxies |
| 44 | +with LoLa/SHM binding. This already provides us with the underlying shared-memory storage! |
| 45 | +If we later discover, that using high-level proxy/skeleton abstraction isn't flexible enough (gives too many constraints), |
| 46 | +we can refactor in a way to use `lib/memory/shared` directly to build a better tailored shared-memory DATA channel |
| 47 | + |
| 48 | +### CONTROL channel |
| 49 | + |
| 50 | +The CONTROL channel is used to notify events occurring at one layer to the adjacent layer (upper layer to lower layer |
| 51 | +and vice versa). These events may be related to: |
| 52 | + |
| 53 | +- DATA updates (notification, that event/field/method data has been updated) |
| 54 | +- service-discovery activities (initiating find/search, discovery results) |
| 55 | +- subscription activities |
| 56 | + |
| 57 | +The CONTROL channel will be implemented based on `score::message_passing`, which is located [here](https://github.com/eclipse-score/communication/tree/main/score/message_passing). |
| 58 | +`score::message_passing` provides: |
| 59 | + |
| 60 | +- unidirectional messaging from client to server |
| 61 | +- unidirectional messaging with asynchronous reply from the server side |
| 62 | +- synchronous RPC style communication. |
| 63 | +- asynchronous notification back from server to client |
| 64 | + |
| 65 | +### `vSomeIP` interface |
| 66 | + |
| 67 | +The main interface being used from `vSomeIP` will be the class `application` |
| 68 | +(see [here](https://github.com/COVESA/vsomeip/blob/master/interface/vsomeip/application.hpp)). Thus, the |
| 69 | +`Network Daemon API` shown in the picture above, will essentially interface to class `vsomeip_v3::application`! |
| 70 | + |
| 71 | +For resource reasons, there will be only one instance of `vsomeip_v3::application` running within the |
| 72 | +`SOME/IP network daemon`. |
| 73 | + |
| 74 | +### Usage of generic proxies and skeletons |
| 75 | + |
| 76 | +As described in [DATA channel](#data-channel), we will use off-the-shelf `score::mw::com` proxies/skeletons to realize |
| 77 | +the DATA channel. Since we gain much more flexibility in these gateway biased use cases, when using loosely typed proxies |
| 78 | +and skeletons, this proposal relies on `score::mw::com::GenericProxy` (already existing) and |
| 79 | +`score::mw::com::GenericSkeleton` (to be developed) instances. |
| 80 | + |
| 81 | +### Service-Discovery usage |
| 82 | + |
| 83 | +From a functional perspective, we could do **without** usage of a service-discovery as the proxies/skeletons are solely |
| 84 | +used in a one-to-one relation and the existing CONTROL channel implicitly informs about the existence of the |
| 85 | +`score::mw::com::GenericSkeleton` providing the DATA channel. |
| 86 | + |
| 87 | +But since a lot of the proxy side functionality is deeply coupled with service-discovery functionality, bypassing it |
| 88 | +seems to generate a lot of effort! Thus, we will use existing `OfferService` (skeleton) and `FindService` (proxy) |
| 89 | +functionality. |
| 90 | + |
| 91 | +## Service instance forwarding to SOME/IP network daemon |
| 92 | + |
| 93 | +### SOME/IP service instance identification |
| 94 | + |
| 95 | +When interacting with SOME/IP network daemon (which encapsulates `vsomeip_v3::application`) we introduce the type |
| 96 | +`SomeIpServiceInstanceId`, which consists of the tuple of: |
| 97 | + |
| 98 | +- service_t |
| 99 | +- instance_t |
| 100 | +- major_version_t |
| 101 | +- minor_version_t |
| 102 | + |
| 103 | +which come from `vsomeip_v3::application`. |
| 104 | + |
| 105 | +### Steps taken to offer local service instance towards SOME/IP and forward data |
| 106 | + |
| 107 | +These are the steps, to forward a local service instance to the SOME/IP network daemon so that it gets provided as a |
| 108 | +SOME/IP service instance on the network: |
| 109 | + |
| 110 | +1. The upper layer creates the DATA channel for the service instance by creating a corresponding |
| 111 | + `score::mw::com::GenericSkeleton` with LoLa/SHM binding. |
| 112 | + It might be sufficient, that each event/field contained in this skeleton has only one sample slot. This would implicitly |
| 113 | + mean, that we will see a data-loss, when the upper layer wants to update an event/field, but the `SOME/IP network daemon` |
| 114 | + is still busy/accessing the single slot. |
| 115 | +2. Then the upper layer sends a message via `score::message_passing` to the `SOME/IP network daemon`, to create the |
| 116 | + SOME/IP service instance. Thus, the message contains a tag/message-id `CreateServiceInstance`. Further payload of this |
| 117 | + message: |
| 118 | + - `SomeIpServiceInstanceId` describing the service instance |
| 119 | + - LoLa service type and instance id, which allows the `SOME/IP network daemon` to create the corresponding proxy instance |
| 120 | + to the skeleton created in (1). |
| 121 | +3. The `SOME/IP network daemon` receives the message in (2) and creates the proxy from the information contained. I.e. it |
| 122 | + issues a `score::mw::com::GenericProxy::FindService` and expects to get instantly a handle back, from which it then |
| 123 | + creates the proxy instance. |
| 124 | + Afterward, it is capable of accessing the event/field slots: The DATA channel is now set up. |
| 125 | +4. The upper layer sends a message via `score::message_passing` to the `SOME/IP network daemon`, to offer the service |
| 126 | + instance created in (1). Thus, the message contains a tag/message-id `OfferServiceInstance`. Further payload of |
| 127 | + this message: |
| 128 | + - `SomeIpServiceInstanceId` describing the service instance |
| 129 | +5. The `SOME/IP network daemon`receives the message sent in (4) and then calls `vsomeip_v3::application::offer_event()` |
| 130 | + for each event/field the underlying proxy created in (3) contains and then finally calls |
| 131 | + `vsomeip_v3::application::offer_service()` with the args taken from `SomeIpServiceInstanceId`. |
| 132 | +6. After the upper layer sends/updates an event/field on the `score::mw::com` skeleton, it sends a message via |
| 133 | + `score::message_passing` to the `SOME/IP network daemon`, to push the update to the SOME/IP service instance. Thus, |
| 134 | + the message contains a tag/message-id `UpdateEvent`. Further payload of this message: |
| 135 | + - `SomeIpServiceInstanceId` describing the service instance. |
| 136 | + - `EventId` describing which event/field to update |
| 137 | +7. The `SOME/IP network daemon`receives the message sent in (6) and then calls `vsomeip_v3::application::send()` |
| 138 | + for the event/field identified by `EventId`. The payload to hand over will be created by accessing the corresponding |
| 139 | + event/field of the `score::mw::com` proxy via `GetNewSamples()`. I.e. only, when `ProxyEvent::GetNewSamples()` |
| 140 | + returns a new `SamplePtr`, a `std::shared_ptr<message>` will be created from it and handed over to the call of |
| 141 | + `vsomeip_v3::application::send()`. After the call returned, the `SamplePtr` can be disposed. |
| 142 | + |
| 143 | +### Steps taken to stop-offer service instance |
| 144 | + |
| 145 | +In case the upper layer only wants to forward a "stop-offer" for its local `score::mw::com` skeleton, it sends a message |
| 146 | +via `score::message_passing` to the `SOME/IP network daemon`, to stop the service instance offering. The message |
| 147 | +contains a tag/message-id `StopOfferServiceInstance`. Further payload of this message: |
| 148 | +- `SomeIpServiceInstanceId` describing the service instance |
| 149 | + The implementation in `SOME/IP network daemon`receives the message and calls |
| 150 | +- `vsomeip_v3::application::stop_offer_service()` with the args taken from `SomeIpServiceInstanceId`. |
| 151 | + |
| 152 | +## Service instance reception from SOME/IP network daemon |
| 153 | + |
| 154 | +### Steps taken to receive a SOME/IP service-instance and provide it locally |
| 155 | + |
| 156 | +1. The upper layer sends a message via `score::message_passing` to the `SOME/IP network daemon`, to trigger the search/ |
| 157 | + discovery for the service instance on the SOME/IP (network) side. Thus, the message contains a tag/message-id |
| 158 | + `DiscoverServiceInstance`. Further payload of this message: |
| 159 | + - `SomeIpServiceInstanceId` describing the service instance |
| 160 | + - detailed meta-info of all events/fields/methods, which is sufficient, that the `SOME/IP network daemon` can create |
| 161 | + a `score::mw::com::GenericSkeleton` from it. |
| 162 | +2. The `SOME/IP network daemon` receives the message in (1) and then calls |
| 163 | + `vsomeip_v3::application::register_availability_handler`. All arguments but one are filled from |
| 164 | + `SomeIpServiceInstanceId`. Last argument is the availability handler (`availability_handler_t`), created by the |
| 165 | + `SOME/IP network daemon`. |
| 166 | +3. As soon as the availability handler fires and indicates, that on SOME/IP the instance identified via |
| 167 | + `SomeIpServiceInstanceId` is available, the `SOME/IP network daemon` creates a `score::mw::com::GenericSkeleton` |
| 168 | + based on the arguments received in (1) and sends back a notification to the upper layer via `score::message_passing` |
| 169 | + (`IServerConnection::Notify()`). Thus, the message contains a tag/message-id `ServiceInstanceAvailable`. Further |
| 170 | + payload of this message: |
| 171 | + - `SomeIpServiceInstanceId` describing the service instance |
| 172 | + - `InstanceIdentifier`. describing the created `score::mw::com::GenericSkeleton` instance |
| 173 | +4. Then the `SOME/IP network daemon` calls `vsomeip_v3::application::register_message_handler` for each event/field of |
| 174 | + the service instance to register its handler for SOME/IP message reception. |
| 175 | +5. When the upper layer receives the notification sent in (3), it calls `score::mw::com::GenericProxy::FindService()` |
| 176 | + with the `InstanceIdentifier` taken from the message and then creates `GenericProxy` instance from the returned |
| 177 | + handle. It now has a "connected" proxy to the SOME/IP service instance. |
| 178 | +6. When an event/field update happens for the network side SOME/IP service instance identified by `SomeIpServiceInstanceId`, |
| 179 | + the `SOME/IP network daemon` gets notified via its handler, which it has registered in (4). In its registered handler |
| 180 | + the `SOME/IP network daemon` accesses the received message payload containing event/field data update and calls the |
| 181 | + corresponding `GenericSkeletonEvent`/`GenericSkeletonField` `Send`/`Update` method. |
| 182 | +7. The upper layer can then – via the GenericProxy it has created in (5) – access updated event/field data |
| 183 | + with the standard mechanisms: (polling based or event notified) `GetNewSamples()` |
0 commit comments