-
Notifications
You must be signed in to change notification settings - Fork 76
Draft someip-network-daemon architecture #2342
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the main message of this sequence diagram? It is a bit confusing to me. Is it maybe detailed design? Then we should remove it from here and move it to the module documentation.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry - work in progress! This is the reason, why it isn't yet referenced from the README.md (no svg generated from it) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| @startuml sequence_provide_someip_service | ||
| title "Sequence Provide SOME/IP service" | ||
| hide footbox | ||
|
|
||
| participant "<u>UpperLayer</u>" as UpperLayer | ||
| participant "<u>NetworkDaemonAPI</u>" as NetworkDaemonAPI | ||
| participant "<u>vsomeip_v3::application</u>" as Application | ||
|
|
||
| activate UpperLayer | ||
| UpperLayer -> NetworkDaemonAPI : message CreateSomeIPService | ||
|
|
||
| activate Runtime | ||
| activate ConfigParser | ||
|
|
||
| opt user wants to specify non-default manifest path | ||
| Process -> Runtime : Initialize(int argc, char** argv) | ||
| activate Process | ||
| deactivate OS | ||
|
|
||
| group config creation | ||
| note over Runtime, ConfigParser: sequence see below | ||
| end group | ||
|
|
||
| Runtime --> Process : return: void | ||
| end opt | ||
|
|
||
| Process -> Runtime : getInstance() | ||
|
|
||
| opt config creation if singleton instance not yet created | ||
| Runtime -> Runtime : Initialize() //with default manifest path | ||
| Runtime -> ConfigParser : parse(path, const serviceTypeRegistry*) | ||
| create Configuration | ||
| ConfigParser -> Configuration : create() | ||
|
|
||
| ConfigParser --> Runtime : return: Configuration | ||
| deactivate ConfigParser | ||
|
|
||
| create RuntimeInstance | ||
| Runtime -> RuntimeInstance : create(configuration) | ||
| end opt | ||
|
|
||
| Runtime --> Process : return: singleton instance | ||
| deactivate Runtime | ||
|
|
||
| Process -> RuntimeInstance : resolve(InstanceSpecifier) | ||
| activate RuntimeInstance | ||
| RuntimeInstance -> Configuration : serviceInstances_.find(InstanceSpecifier) | ||
| activate Configuration | ||
| Configuration --> RuntimeInstance : return: Iter | ||
| deactivate Configuration | ||
|
|
||
| RuntimeInstance --> Process : return: InstanceIdentifierContainer | ||
| deactivate RuntimeInstance | ||
| deactivate Process | ||
|
|
||
| @enduml |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,183 @@ | ||||||
| # SOME/IP Network Daemon Design Proposal | ||||||
|
|
||||||
| ## Intro | ||||||
|
|
||||||
| Because of safety considerations we have a specific `SOME/IP network daemon`, which is responsible to handle the SOME/IP | ||||||
| related network communication. | ||||||
| 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). | ||||||
|
|
||||||
| Within S-CORE architecture, there will be different upper layers, which all need to use this network daemon. Currently | ||||||
| under discussion: | ||||||
| - Generic SOME/IP Gateway Daemon | ||||||
| - OEM specific SOME/IP Gateway Daemon | ||||||
| - S-CORE application using `score::mw::com` with a specific SOME/IP binding | ||||||
|
|
||||||
| The interfacing with the `SOME/IP network daemon` shall be identical for all upper layers. | ||||||
| In this proposal we define a common architecture, which should end up in re-usable building blocks/libs, which are then | ||||||
| used by all upper layers uniformly. | ||||||
|
|
||||||
| ## Basic Architecture | ||||||
|
|
||||||
| There exist two "channels" between the `SOME/IP network daemon` and an upper layer using/relying on it: | ||||||
|
|
||||||
| - a DATA channel | ||||||
| - a CONTROL channel | ||||||
|
|
||||||
| we explicitly separate these two channels as we intend to implement them with different technical mechanisms. | ||||||
|
|
||||||
|  | ||||||
|
|
||||||
| ### DATA channel | ||||||
|
|
||||||
| The DATA channel transports the (SOME/IP) service instance related data. This is: | ||||||
|
|
||||||
| - Event payloads | ||||||
| - Field payloads | ||||||
| - Method payloads (in-arguments and return values) | ||||||
|
|
||||||
| We intend to use shared-memory to implement the DATA channel. Mainly because payloads listed above might be large and | ||||||
| using shared-memory as exchange mechanism might save copies in certain cases, which helps with performance. | ||||||
| Additionally: Since there are already existing building blocks for shared-memory communication either in the more high | ||||||
| level form of the `score::mw::com` SHM/LoLa binding or in its underlying `lib/memory/shared`. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so it will be on top of mw::com and not below?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK. You definitely have a point here! The thing is, that - depending on the usage scenario - you get something, which smells like a "cyclic dependency", if the functionality to interface with the vsomeip-network-daemon itself relies on Let me elaborate a bit:
I do not see some weird "cyclic dependency". These gateway daemon apps/processes, will use In the other usage scenario, where we would deploy this vsomeip-interface-lib in a binding within the So this imho is really discussion worthy ... whether it is a good idea to base the implementation of this interfacing library on |
||||||
|
|
||||||
| Initially we plan implement this by DATA channel via creation of corresponding `score::mw::com` skeletons and proxies | ||||||
| with LoLa/SHM binding. This already provides us with the underlying shared-memory storage! | ||||||
| If we later discover, that using high-level proxy/skeleton abstraction isn't flexible enough (gives too many constraints), | ||||||
| we can refactor in a way to use `lib/memory/shared` directly to build a better tailored shared-memory DATA channel | ||||||
|
|
||||||
| ### CONTROL channel | ||||||
|
|
||||||
| The CONTROL channel is used to notify events occurring at one layer to the adjacent layer (upper layer to lower layer | ||||||
| and vice versa). These events may be related to: | ||||||
|
|
||||||
| - DATA updates (notification, that event/field/method data has been updated) | ||||||
| - service-discovery activities (initiating find/search, discovery results) | ||||||
| - subscription activities | ||||||
|
|
||||||
| 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). | ||||||
| `score::message_passing` provides: | ||||||
|
|
||||||
| - unidirectional messaging from client to server | ||||||
| - unidirectional messaging with asynchronous reply from the server side | ||||||
| - synchronous RPC style communication. | ||||||
| - asynchronous notification back from server to client | ||||||
|
|
||||||
| ### `vSomeIP` interface | ||||||
|
|
||||||
| The main interface being used from `vSomeIP` will be the class `application` | ||||||
| (see [here](https://github.com/COVESA/vsomeip/blob/master/interface/vsomeip/application.hpp)). Thus, the | ||||||
| `Network Daemon API` shown in the picture above, will essentially interface to class `vsomeip_v3::application`! | ||||||
|
|
||||||
| For resource reasons, there will be only one instance of `vsomeip_v3::application` running within the | ||||||
| `SOME/IP network daemon`. | ||||||
|
Comment on lines
+71
to
+72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In order to calculate the E2E checksum the Request ID [Client ID | Session ID] needs to be known at the ASIL side. Therefore it would make sense to calculate the Session ID at the ASIL side. That can only be done if each ASIL client gets a separate Client ID because otherwise two ASIL clients calling the same method via SOME/IP would cause a conflict. As far as I can grasp from the documentation of
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm. I guess you are right :( ... the application::send(), which is used to send a request (do a message call) clearly states, that client-id/session-id is taken from the This is really "awful" then - if the documentation of class
we have to swallow this pill? |
||||||
|
|
||||||
| ### Usage of generic proxies and skeletons | ||||||
|
|
||||||
| As described in [DATA channel](#data-channel), we will use off-the-shelf `score::mw::com` proxies/skeletons to realize | ||||||
| the DATA channel. Since we gain much more flexibility in these gateway biased use cases, when using loosely typed proxies | ||||||
| and skeletons, this proposal relies on `score::mw::com::GenericProxy` (already existing) and | ||||||
| `score::mw::com::GenericSkeleton` (to be developed) instances. | ||||||
|
|
||||||
| ### Service-Discovery usage | ||||||
|
|
||||||
| From a functional perspective, we could do **without** usage of a service-discovery as the proxies/skeletons are solely | ||||||
| used in a one-to-one relation and the existing CONTROL channel implicitly informs about the existence of the | ||||||
| `score::mw::com::GenericSkeleton` providing the DATA channel. | ||||||
|
|
||||||
| But since a lot of the proxy side functionality is deeply coupled with service-discovery functionality, bypassing it | ||||||
| seems to generate a lot of effort! Thus, we will use existing `OfferService` (skeleton) and `FindService` (proxy) | ||||||
| functionality. | ||||||
|
Comment on lines
+87
to
+89
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What would we need to simplify this? Can we make an issue to solve that? |
||||||
|
|
||||||
| ## Service instance forwarding to SOME/IP network daemon | ||||||
|
|
||||||
| ### SOME/IP service instance identification | ||||||
|
|
||||||
| When interacting with SOME/IP network daemon (which encapsulates `vsomeip_v3::application`) we introduce the type | ||||||
| `SomeIpServiceInstanceId`, which consists of the tuple of: | ||||||
|
|
||||||
| - service_t | ||||||
| - instance_t | ||||||
| - major_version_t | ||||||
| - minor_version_t | ||||||
|
|
||||||
| which come from `vsomeip_v3::application`. | ||||||
|
|
||||||
| ### Steps taken to offer local service instance towards SOME/IP and forward data | ||||||
|
|
||||||
| These are the steps, to forward a local service instance to the SOME/IP network daemon so that it gets provided as a | ||||||
| SOME/IP service instance on the network: | ||||||
|
|
||||||
| 1. The upper layer creates the DATA channel for the service instance by creating a corresponding | ||||||
| `score::mw::com::GenericSkeleton` with LoLa/SHM binding. | ||||||
| It might be sufficient, that each event/field contained in this skeleton has only one sample slot. This would implicitly | ||||||
| mean, that we will see a data-loss, when the upper layer wants to update an event/field, but the `SOME/IP network daemon` | ||||||
| is still busy/accessing the single slot. | ||||||
| 2. Then the upper layer sends a message via `score::message_passing` to the `SOME/IP network daemon`, to create the | ||||||
| SOME/IP service instance. Thus, the message contains a tag/message-id `CreateServiceInstance`. Further payload of this | ||||||
| message: | ||||||
| - `SomeIpServiceInstanceId` describing the service instance | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably we need more config data (event ids, event groups, ports, etc.). How do we exchange that? Should each upper layer initially send a registration message which then spins up the application instance? The registration message could contain a flatbuffer with all of the config data and the reply could contain data like the Client ID assigned to the upper layer. |
||||||
| - LoLa service type and instance id, which allows the `SOME/IP network daemon` to create the corresponding proxy instance | ||||||
| to the skeleton created in (1). | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why someipdeamon part uses lola and not mw::com ? |
||||||
| 3. The `SOME/IP network daemon` receives the message in (2) and creates the proxy from the information contained. I.e. it | ||||||
| issues a `score::mw::com::GenericProxy::FindService` and expects to get instantly a handle back, from which it then | ||||||
| creates the proxy instance. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isnt mw::com FindService based on instance specifier and not lola ids ? 2/3 looks confusing but maybe I miss something? |
||||||
| Afterward, it is capable of accessing the event/field slots: The DATA channel is now set up. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The network daemon should send a reply. For methods it needs to create a DATA skeleton used to communicate back responses and return that LoLa instance id to the upper layer. In general it might be useful for the upper layer to know when the setup is complete.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By looking at the current state of this PR/doc AND your comment I feel, that the documentation/these detailed steps described, somewhat miss, what the API interface to offer a service instance with the vsomeip-app shall look like ... I.e. it directly starts with some "internal" implementation steps, without outlining 1st, how the API shall look like, which the upper layer calls ... and since this is missing and we therefore don't know what return code/error-code we shall support, things which you are mentioning here
So maybe it makes more sense to design our vsomeip-wrapper-API signatures first? ... |
||||||
| 4. The upper layer sends a message via `score::message_passing` to the `SOME/IP network daemon`, to offer the service | ||||||
| instance created in (1). Thus, the message contains a tag/message-id `OfferServiceInstance`. Further payload of | ||||||
| this message: | ||||||
| - `SomeIpServiceInstanceId` describing the service instance | ||||||
| 5. The `SOME/IP network daemon`receives the message sent in (4) and then calls `vsomeip_v3::application::offer_event()` | ||||||
| for each event/field the underlying proxy created in (3) contains and then finally calls | ||||||
| `vsomeip_v3::application::offer_service()` with the args taken from `SomeIpServiceInstanceId`. | ||||||
| 6. After the upper layer sends/updates an event/field on the `score::mw::com` skeleton, it sends a message via | ||||||
| `score::message_passing` to the `SOME/IP network daemon`, to push the update to the SOME/IP service instance. Thus, | ||||||
| the message contains a tag/message-id `UpdateEvent`. Further payload of this message: | ||||||
| - `SomeIpServiceInstanceId` describing the service instance. | ||||||
| - `EventId` describing which event/field to update | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
In AUTOSAR SOME/IP the "Method ID" is used for all of the components. Methods have a Method ID in the range [0-0x7FFF] and events/fields have a Method ID in the range [0x8000-0xFFFF]. At least that is what is the nomenclature in the AUTOSAR specification. In the Open SOME/IP specification they introduced the Method ID and Event ID which is then the AUTOSAR Method ID without the highest bit. I mean it was confusing before, but now we have the same name used for different stuff in each specification. Possible alternative terms: |
||||||
| 7. The `SOME/IP network daemon`receives the message sent in (6) and then calls `vsomeip_v3::application::send()` | ||||||
| for the event/field identified by `EventId`. The payload to hand over will be created by accessing the corresponding | ||||||
| event/field of the `score::mw::com` proxy via `GetNewSamples()`. I.e. only, when `ProxyEvent::GetNewSamples()` | ||||||
| returns a new `SamplePtr`, a `std::shared_ptr<message>` will be created from it and handed over to the call of | ||||||
| `vsomeip_v3::application::send()`. After the call returned, the `SamplePtr` can be disposed. | ||||||
|
|
||||||
| ### Steps taken to stop-offer service instance | ||||||
|
|
||||||
| In case the upper layer only wants to forward a "stop-offer" for its local `score::mw::com` skeleton, it sends a message | ||||||
| via `score::message_passing` to the `SOME/IP network daemon`, to stop the service instance offering. The message | ||||||
| contains a tag/message-id `StopOfferServiceInstance`. Further payload of this message: | ||||||
| - `SomeIpServiceInstanceId` describing the service instance | ||||||
| The implementation in `SOME/IP network daemon`receives the message and calls | ||||||
| - `vsomeip_v3::application::stop_offer_service()` with the args taken from `SomeIpServiceInstanceId`. | ||||||
|
|
||||||
| ## Service instance reception from SOME/IP network daemon | ||||||
|
|
||||||
| ### Steps taken to receive a SOME/IP service-instance and provide it locally | ||||||
|
|
||||||
| 1. The upper layer sends a message via `score::message_passing` to the `SOME/IP network daemon`, to trigger the search/ | ||||||
| discovery for the service instance on the SOME/IP (network) side. Thus, the message contains a tag/message-id | ||||||
| `DiscoverServiceInstance`. Further payload of this message: | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not call it |
||||||
| - `SomeIpServiceInstanceId` describing the service instance | ||||||
| - detailed meta-info of all events/fields/methods, which is sufficient, that the `SOME/IP network daemon` can create | ||||||
| a `score::mw::com::GenericSkeleton` from it. | ||||||
| 2. The `SOME/IP network daemon` receives the message in (1) and then calls | ||||||
| `vsomeip_v3::application::register_availability_handler`. All arguments but one are filled from | ||||||
| `SomeIpServiceInstanceId`. Last argument is the availability handler (`availability_handler_t`), created by the | ||||||
| `SOME/IP network daemon`. | ||||||
| 3. As soon as the availability handler fires and indicates, that on SOME/IP the instance identified via | ||||||
| `SomeIpServiceInstanceId` is available, the `SOME/IP network daemon` creates a `score::mw::com::GenericSkeleton` | ||||||
| based on the arguments received in (1) and sends back a notification to the upper layer via `score::message_passing` | ||||||
| (`IServerConnection::Notify()`). Thus, the message contains a tag/message-id `ServiceInstanceAvailable`. Further | ||||||
| payload of this message: | ||||||
| - `SomeIpServiceInstanceId` describing the service instance | ||||||
| - `InstanceIdentifier`. describing the created `score::mw::com::GenericSkeleton` instance | ||||||
| 4. Then the `SOME/IP network daemon` calls `vsomeip_v3::application::register_message_handler` for each event/field of | ||||||
| the service instance to register its handler for SOME/IP message reception. | ||||||
| 5. When the upper layer receives the notification sent in (3), it calls `score::mw::com::GenericProxy::FindService()` | ||||||
| with the `InstanceIdentifier` taken from the message and then creates `GenericProxy` instance from the returned | ||||||
| handle. It now has a "connected" proxy to the SOME/IP service instance. | ||||||
| 6. When an event/field update happens for the network side SOME/IP service instance identified by `SomeIpServiceInstanceId`, | ||||||
| the `SOME/IP network daemon` gets notified via its handler, which it has registered in (4). In its registered handler | ||||||
| the `SOME/IP network daemon` accesses the received message payload containing event/field data update and calls the | ||||||
| corresponding `GenericSkeletonEvent`/`GenericSkeletonField` `Send`/`Update` method. | ||||||
| 7. The upper layer can then – via the GenericProxy it has created in (5) – access updated event/field data | ||||||
| with the standard mechanisms: (polling based or event notified) `GetNewSamples()` | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what with set/get on fields coming from outside? Do we support fields really (is there a way currently to attach a setter for a field as internal mw:com is pure pub-sub. same with get I suppose ) ? |
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: It should be "Daemon" not "Deamon"