Spécification de la langue

Cette page spécifie le langage de définition d'interface du service de véhicule (VSIDL) à l'aide de la forme Backus-Naur étendue (EBNF) ISO/IEC 14977 et de protobuf. Cette page se concentre sur la grammaire non contextuelle du langage et sur la signification des éléments du langage.

Hiérarchie des langues

Selon la Meta Object Facility (MOF), le compilateur VSIDL (VSIDLC) utilise les langages illustrés dans la figure 1 :

Langages VSIDLC

Figure 1. Langages VSIDLC.

VSIDLC est principalement basé sur le langage des tampons de protocole (protobuf). Protobuf spécifie les types de données échangés à l'aide de la publication/abonnement et des appels de procédure à distance (RPC). Techniquement, les modèles VSIDL sont des fichiers TextProto, et la syntaxe VSIDL est définie dans un fichier protobuf (syntax.proto).

Les fichiers protobuf permettant de spécifier les types de données et les modèles VSIDL sont tous deux utilisés pour générer du code Rust. Le code généré contient des structures pour les structures de données de message échangées et des fonctions Rust pour créer des unités de service, sans appeler ces unités de service automatiquement. Le code Rust personnalisé utilise ensuite le code généré pour instancier les unités de service et mettre en œuvre la logique d'application.

Syntaxe abstraite

La figure 2 illustre les principaux types de messages dans VSIDL :

Principaux types de messages dans VSIDL

Figure 2. Types de messages principaux dans VSIDL.

Entrée VSIDL

Cette section explique le type de message d'entrée VSIDL.

Grammaire EBNF

start VsidlEntry =
  "package" , ":" , String , ";" ,
  { "service_bundle" , ServiceBundle } ,
  { "extension" , ":" , Any } ,
  { "some_ip_mapping" , ":" , SomeIpMapping } ,
  { "vhal_mapping" , ":" , VhalMapping }
;

Définition du proto

// The root message for VSIDL files
message VsidlEntry {
  // Required. Package name for entities mentioned in the file.
  string package = 1;
  // Enables custom extensions beyond the standard VSIDL model.
  repeated google.protobuf.Any extension = 3;

  // SOMEIP mapping rules
  repeated sdv.someip.v1.SomeIpMapping some_ip_mapping = 4;
  // VHAL mapping rules
  repeated VhalMapping vhal_mapping = 5;
  // List of SDV service bundles defined in the file.
  repeated ServiceBundle service_bundle = 6;
}

Exemple d'utilisation

package: "com.android.sdv.sample.vsidl"

service_bundle {
  name: "Manager"

  publisher {
    message: "TirePressure"
    topic: "front-left"
    topic: "front-right"
    capacity: 10
  }
}

Explication

Le message VsidlEntry sert de conteneur racine pour le fichier VSIDL (avec l'extension .vsidl). Ce message encapsule toutes les définitions et configurations dans un seul fichier VSIDL. VsidlEntry est l'élément de premier niveau qui lie tous les autres.

Objectif :

  • Définit la structure globale d'un fichier VSIDL.
  • Spécifie l'espace de noms du package pour toutes les entités du fichier.
  • Contient une collection de définitions de bundles de services.
  • Permet d'ajouter des extensions personnalisées au modèle VSIDL.
  • Inclut des règles de mappage pour SOME/IP et VHAL.

Contraintes

  • Nom du package (E211) : le nom du package ne doit pas dépasser 127 caractères.
  • Fichiers non référencés (E10B) : tous les fichiers d'un catalogue doivent être référencés dans un groupe de fichiers Android.bp.

Pack de services

Cette section explique le type de message de bundle de services.

Grammaire EBNF

ServiceBundle = "{" , { ServiceBundleElement } , "}" ;

ServiceBundleElement =
  "name" , ":" , String |
  "publisher" , Publisher |
  "subscriber" , Subscriber |
  "server" , Server |
  "client" , Client |
  "extension" , ":" , Any |
  "diagnostics_declaration" , DiagnosticsDeclaration |
  "build_cfg" , BuildConfiguration |
  "register_reflection_metadata" , Boolean
;

Définition du proto

// Defines an SDV service
message ServiceBundle {
  // Required. Name of the service bundle (without the package name).
  string name = 1;
  // List of publications the service bundle provides.
  repeated Publisher publisher = 2;
  // List of publications a service bundle subscribes to.
  repeated Subscriber subscriber = 3;
  // RPC services offered by a service bundle.
  repeated Server server = 4;
  // RPC services consumed by a service bundle.
  repeated Client client = 5;
  // Enables custom extensions beyond the standard VSIDL model.
  repeated google.protobuf.Any extension = 7;

  // Diagnostics declarations
  sdv.diagnostics.v1.DiagnosticsDeclaration diagnostics_declaration = 8;

  // Build Configuration
  optional BuildConfiguration build_cfg = 9;

  // Register metadata for service units provided by this service bundle.
  // Setting this to true will increase the memory footprint
  // and network load significantly.
  bool register_reflection_metadata = 10;
}

Exemple d'utilisation

service_bundle {
    name: "SeatController"

    publisher {
      message: "SeatHeating"
      topic: "driver-seat"
      capacity: 10
    }
}

Explication

Un bundle de services définit un regroupement logique de services, d'éditeurs, d'abonnés, de serveurs RPC et de clients RPC associés. Un groupe de services sert de conteneur pour un ensemble spécifique de fonctionnalités et leurs interactions.

Contraintes

  • Exigences concernant le nom du bundle (E209, E20A, E20B, E20C) :
    • Un bundle de services doit avoir un nom renseigné.
    • Le nom doit commencer par un caractère de début d'identifiant Unicode valide (généralement une lettre).
    • Les caractères suivants du nom doivent être des caractères de continuation d'identifiant Unicode valides (généralement des lettres ou des chiffres).
    • Le nom ne doit pas être un mot clé réservé en Rust, Java ou C++.
  • Unicité globale des bundles (E309) : chaque bundle de services doit avoir un nom complet unique (basé sur son package et son nom).
  • Unicité interne (E100, E300, E302, E303, E308) :
    • Dans un seul bundle de services, chaque service RPC peut être diffusé par une définition de serveur au maximum.
    • Dans un même groupe de services, chaque type de publication MULTI_PUB peut être publié par une seule définition d'éditeur au maximum.
    • Dans un même bundle de services, tous les noms d'unités de service définis par l'utilisateur (pour les éditeurs ou les serveurs) doivent être uniques.
    • Dans un même bundle de services, tous les noms d'unités de service (qu'ils soient définis par l'utilisateur ou générés automatiquement) doivent être uniques.
  • Conventions d'attribution de noms des cibles de compilation (E205, E206, E207, E208) : si un nom de cible de compilation personnalisé (build_cfg.target_name) est fourni, il doit respecter le format snake_case (lettres minuscules, chiffres et traits de soulignement uniques, ne commençant ni ne se terminant par un trait de soulignement).
  • Unicité du nom de la cible de compilation (E301) : un nom de cible de compilation défini par l'utilisateur ne doit pas entrer en conflit avec les noms de cibles générés automatiquement pour d'autres bundles de services.

Éditeur

Cette section explique le type de message Publisher.

Grammaire EBNF

Publisher = "{" , { PublisherElement } , "}" ;

PublisherElement =
  "message" , ":" , String |
  "topic" , ":" , String |
  "capacity" , ":" , Integer |
  "service_unit_name" , ":" , String
;

Définition du proto

// Represents a publisher within a service bundle.
message Publisher {
  // Name of the service unit. Name may only use characters from [a-z0-9\-]+,
  // must start with [a-z], may not end with a hyphen,
  // and may not contain consecutive hyphens.
  string service_unit_name = 3;
  // Required. The type of data being published.
  string message = 4;
  // Required. The number of messages a publication queue can hold.
  // Must be an even number >= 2.
  int64 capacity = 6;
  // Required. Unique identifier for the publication topic.
  // Must be in lowercase dash-case.
  repeated string topic = 7;
}

Exemple d'utilisation

publisher {
  message: "SeatHeating"
  topic: "driver-heating"
  capacity: 10
}

Explication

Le type de message Publisher définit une source de données fournie par ServiceBundle. Ce type de message spécifie le type de données publiées, ainsi que les sujets spécifiques et la capacité de ces données.

Thèmes

Chaque instance Publisher comporte un champ message qui fait référence au message proto en cours de publication. Il doit spécifier un sujet (représenté par topic) et une capacité (représentée par capacity).

  • Sujet : identifiant unique du sujet de publication. Il doit respecter le format "dash-case" en minuscules (par exemple, my-topic).
  • Capacité : spécifie la taille de la file d'attente, c'est-à-dire le nombre de messages qu'elle peut contenir avant que les messages non lus ne soient supprimés. Il doit s'agir d'un nombre pair supérieur ou égal à 2.

Noms définis par l'utilisateur

Les éditeurs peuvent définir des noms d'unités de service qui remplacent les noms d'unités de service choisis automatiquement. Cette affordance permet de choisir des noms plus courts. Si un éditeur utilise un nom d'unité de service défini par l'utilisateur, il peut n'utiliser qu'une seule instance, de sorte que le nom de l'unité de service soit attribué de manière unique à une instance.

# VALID: A publisher assigns a user-defined name to a single instance
publisher {
  message: "SeatHeating"
  topic: "seat-heating-status"
  service_unit_name: "heating-is-off"
}

# ERROR: user-defined names are only allowed if there's only a single instance
publisher {
  message: "SeatHeating"
  topic: "seat-heating-status"
  service_unit_name: "heating-status"
}

Contraintes

  • Emplacement de l'éditeur (E300) : les éditeurs du même type MULTI_PUB doivent être définis dans des bundles de services distincts.
  • Unicité du nom local (E302) : dans un même groupe de services, tous les éditeurs doivent avoir des noms d'unités de service uniques définis par l'utilisateur.
  • Unicité globale des noms (E304) : les éditeurs du même type de publication doivent avoir des noms d'unités de service définis par l'utilisateur uniques dans tous les bundles de services.
  • Nommer des canaux uniques (E306) : les noms d'unités de service définis par l'utilisateur ne peuvent être attribués qu'aux éditeurs qui gèrent exactement une instance.
  • Limite d'un seul éditeur (E307) : un message protobuf marqué comme SINGLE_PUB ne peut être publié que par un seul éditeur dans l'ensemble du système.
  • Unicité du nom de l'unité de service (E308) : tous les noms d'unités de service (générés ou définis par l'utilisateur) doivent être uniques au sein de leur groupe de services. Les noms définis par l'utilisateur doivent être utilisés pour résoudre les conflits avec les noms générés.
  • Exigence de spécification de variante (E501) : lorsqu'un éditeur utilise un nom défini par l'utilisateur pour un type comportant plusieurs variantes, il doit spécifier explicitement la variante qu'il publie.
  • Existence d'un éditeur pour les abonnés (E504) : chaque abonné défini nécessite au moins un éditeur correspondant pour le type et la variante spécifiés.
  • Type d'éditeur valide (E601) : un éditeur doit référencer un type qui correspond à un message protobuf existant.
  • Exigence d'annotation de publication (E602) : le type de message protobuf référencé par un éditeur doit inclure l'annotation SdvPublication.
  • Utilisation de variante valide (E606) : si un éditeur spécifie une variante (instance), celle-ci doit exister dans l'instances_enum défini pour le type de publication dans protobuf.
  • Condition de spécification de variante (E607) : un éditeur ne peut spécifier une variante explicite (instance) que si le type de publication définit un instances_enum dans protobuf.
  • Nom des sujets (E20D, E20F) : les sujets doivent être en minuscules, séparés par des tirets et ne pas dépasser 127 caractères.
  • Unicité des thèmes (E314) : les thèmes doivent être uniques pour tous les éditeurs.
  • Exigences de capacité (E406, E407) : la capacité est obligatoire et doit être un nombre pair supérieur ou égal à 2.

Abonné

Cette section explique le type de message "Abonné".

Grammaire EBNF

Subscriber = "{" , { SubscriberElement } , "}" ;

SubscriberElement =
  "message" , ":" , String |
  "topic" , ":" , String
;

Définition du proto

// Represents a subscriber within a service bundle.
message Subscriber {
  // Required. The type of data being subscribed to.
  string message = 4;
  // Required. Specific topic(s) of the message to subscribe to.
  // Must match the publisher's topic.
  repeated string topic = 6;
}

Exemple d'utilisation

subscriber {
  message: "SeatHeating"
  topic: "driver-seat"
}

Explication

Le message Subscriber définit un récepteur de publication fourni par ServiceBundle. Ce message spécifie le type de données auxquelles l'abonnement est souscrit et les thèmes spécifiques de cette publication. S'il existe plusieurs éditeurs pour un sujet, l'abonné reçoit les messages publiés par chacun d'eux.

Contraintes

  • Existence de l'éditeur (E504) : pour chaque abonné défini, il doit exister au moins un éditeur correspondant qui publie le type et la variante de publication spécifiés.
  • Type d'abonnement valide (E608) : un abonné doit référencer un type qui correspond à un message protobuf existant défini avec l'annotation SdvPublication.
  • Abonnement à une variante valide (E609) : si un abonné spécifie une variante (instance), celle-ci doit être une valeur valide définie dans instances_enum du type de publication protobuf correspondant.
  • Thème obligatoire (E408) : le thème est obligatoire pour les abonnés.
  • Redéclaration de sujet (E311) : les sujets d'abonné ne doivent pas être redéclarés dans le même groupe de services.

Serveur RPC

Cette section explique le type de message du serveur RPC.

Grammaire EBNF

Server = "{" , { ServerElement } , "}" ;

ServerElement =
  "service" , ":" , String |
  "channel" , ":" , String |
  "service_unit_name" , ":" , String
;

Définition du proto

// Represents an RPC server within a service bundle.
message Server {
  // Deprecated. Name of the service unit.
  string service_unit_name = 3 [ deprecated = true ];
  // Required. Name of the RPC service.
  string service = 4;
  // Required. Name of the RPC channel.
  // Must be in lowercase dash-case.
  string channel = 5;
}

Exemple d'utilisation

server {
  service: "SetTemperature"
  channel: "temp-setter"
}

Explication

Le message Server définit un serveur RPC fourni par ServiceBundle. Ce message spécifie le service que le serveur implémente et le canal RPC.

Un serveur RPC expose un ensemble de méthodes que les clients peuvent appeler à distance. Le champ service spécifie le nom du service RPC implémenté par le serveur. Ce service est défini dans un fichier proto et implémenté dans du code Rust personnalisé. Les services RPC peuvent inclure des méthodes unaires, de streaming client et de streaming serveur, telles que définies dans la définition du service protobuf. Le champ channel définit le point de terminaison de communication et est obligatoire (E409).

Contraintes

  • Définition du service (E603) : un serveur RPC doit spécifier une valeur service qui correspond à une valeur service RPC protobuf existante.
  • Limite de serveurs par service (E100) : dans un même groupe de services, un RPC service spécifique ne peut être diffusé que par une seule définition de serveur.
  • Nommage des canaux (E20E) : les canaux RPC doivent être en minuscules et séparés par des tirets.
  • Canal obligatoire (E409) : le canal RPC est obligatoire.
  • Unicité du canal (E40B) : le canal RPC ne doit être utilisé que par un seul service.

Client RPC

Cette section explique le type de message du client RPC.

Grammaire EBNF

Client = "{" , { ClientElement } , "}" ;

ClientElement =
  "service" , ":" , String |
  "channel" , ":" , String
;

Définition du proto

// Represents an RPC client within a service bundle.
message Client {
  // Required. Name of the RPC service.
  string service = 2;
  // Required. Name of the RPC channel.
  // Must match the server's channel and be in lowercase dash-case.
  string channel = 3;
}

Exemple d'utilisation

client {
  service: "SetTemperature"
  channel: "temp-setter"
}

Explication

client définit un client RPC que ServiceBundle utilise. client spécifie le service avec lequel le client interagit et le canal auquel se connecter. Le client peut interagir avec des méthodes unaires, de streaming client et de streaming serveur, en fonction de la définition du service.

Contraintes

  • Définition du service (E60A) : un client RPC doit spécifier un service qui correspond à une définition service protobuf existante.
  • Source de service unique (E60B) : la définition protobuf service référencée par le service d'un client RPC doit être définie de manière unique (et non plusieurs fois) dans tous les fichiers protobuf.
  • Canal obligatoire (E409) : le canal RPC est obligatoire.

Configuration de la compilation

Cette section explique le type de message de configuration de compilation.

Grammaire EBNF

BuildConfiguration = "{" , BuildConfigurationElement, "}" ;

BuildConfigurationElement =
  "target_name" , ":" , String |
  "skip_codegen" , ":" , Boolean
;

Définition du proto

// Defines additional information used to configure build settings
message BuildConfiguration {
  /// Build target name
  optional string target_name = 1;
  // Do not generate code for this service bundle
  optional bool skip_codegen = 2;
}

Exemple d'utilisation

build_cfg {
  target_name: "my_custom_target_name"
  skip_codegen: false
}

Explication

BuildConfiguration configure les paramètres non standards de ServiceBundle pour la génération de code. Toutes les configurations de compilation sont facultatives.

  • target_name (string facultatif) : spécifie le nom de la cible de compilation dans les fichiers Android.bp. Utilisez cette option pour définir des noms de cible plus courts que ceux choisis automatiquement.
  • skip_codegen (bool facultatif) : indique si la génération de code doit être ignorée pour ce bundle de services. Si la valeur est définie sur true, aucun code n'est généré pour ce bundle de services particulier. Cela peut être utile pour les bundles de services implémentés manuellement. Par défaut, cette valeur est définie sur false.

Contraintes

  • Format du nom de la cible (E205, E206, E207, E208) : si un nom de cible de compilation personnalisé (build_cfg.target_name) est fourni, il doit respecter strictement le format snake_case :
    • Il ne doit contenir que des lettres minuscules (a à z), des chiffres (0 à 9) et des traits de soulignement (_).
    • Il ne doit pas contenir de traits de soulignement consécutifs (__).
    • Il ne doit pas commencer par un trait de soulignement.
    • Il ne doit pas se terminer par un trait de soulignement.
  • Unicité du nom de la cible (E301) : un build_cfg.target_name défini par l'utilisateur doit être unique dans le système de compilation et ne doit pas entrer en conflit avec les noms de cibles générés automatiquement à partir d'autres définitions de bundles de services.