Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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

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"

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Choose a reason for hiding this comment

The 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.

Copy link
Author

Choose a reason for hiding this comment

The 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.

![Channels](./assets/basic_arch_overview.png)

### 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`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so it will be on top of mw::com and not below?

Copy link
Author

Choose a reason for hiding this comment

The 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 score::mw::com (with SHM/LoLa binding).

Let me elaborate a bit:
In the usage scenarios, where the upper layer/user of this lib is a stand-alone gateway process using score::mw::com skeletons/proxies and also using this lib:

  • Generic SOME/IP Gateway Daemon
  • OEM specific SOME/IP Gateway Daemon

I do not see some weird "cyclic dependency". These gateway daemon apps/processes, will use score::mw::com skeletons/proxies with LoLa/shm binding die interact with the "normal" score::mw::com apps (based on the SHM/LoLa binding). To interface with the vsomeip-network daemon these apps will use our "interface lib". Whether this interface lib internally also uses score::mw::com skeletons/proxies with LoLa/shm binding is transparent to these gateway apps. (Yes there are some potential issues to solve, because in the context of these gateway apps, we then have at least two different mw_com_config.json configs -> one for the communication (lower level) of our lib to interface with the vsomeip-daemon and the other for the config to communicate to the "normal" score::mw::com apps.

In the other usage scenario, where we would deploy this vsomeip-interface-lib in a binding within the score::mw::com (i.e. it will be used somewhere in score::mw::com::impl::bindings::someip this kind of dependency could eventually REALLY turn out as some "cyclic dependency" with awkward side-effects ....

So this imho is really discussion worthy ... whether it is a good idea to base the implementation of this interfacing library on score::mw::com! Maybe we really should already from the beginning go the other route I mentioned above:
Implement the "control-channel" on the stand-alone message passing library and also implement "data channel" with lower level shmare-memory lib ....


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

Choose a reason for hiding this comment

The 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 vsomeip_v3::application::get_client() this would imply that the network daemon has to create a separate application object for each ASIL client.

Copy link
Author

Choose a reason for hiding this comment

The 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 application instance. I.e. one can not explicitly hand it over per send()call!

This is really "awful" then - if the documentation of class application is right:

Due to its heavy resource footprint, it should exist once per client and can be instantiated using the API of @ref runtime. It manages the lifecycle of the vsomeip client and allocates all resources needed to communicate.

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

Choose a reason for hiding this comment

The 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

Choose a reason for hiding this comment

The 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).
Copy link
Contributor

Choose a reason for hiding this comment

The 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.
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Choose a reason for hiding this comment

The 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.

Copy link
Author

Choose a reason for hiding this comment

The 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

In general it might be useful for the upper layer to know when the setup is complete.

So maybe it makes more sense to design our vsomeip-wrapper-API signatures first?
Like:

score::Result<ServiceHandle> VsomeipWrapper::CreateService(someip_service_id, someip_instance_id, DataChannelId)
score::ResultBlank VsomeipWrapper::OfferService(ServiceHandle)

...
and then describe the expected steps been taken within these APIs...

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- `EventId` describing which event/field to update
- `ComponentId` describing which event/field/method to update

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: MemberId, ElementId

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:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not call it FindService?

- `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 &ndash; via the GenericProxy it has created in (5) &ndash; access updated event/field data
with the standard mechanisms: (polling based or event notified) `GetNewSamples()`
Copy link
Contributor

Choose a reason for hiding this comment

The 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 ) ?

Loading