Specyfikacja języka

Na tej stronie opisano język definicji interfejsu usługi pojazdu (VSIDL) przy użyciu rozszerzonej formy Backusa-Naura (EBNF) ISO/IEC 14977 i protobuf. Ta strona skupia się na gramatyce bezkontekstowej języka i znaczeniu jego elementów.

Hierarchia języków

Zgodnie z Meta Object Facility (MOF) kompilator VSIDL (VSIDLC) używa języków pokazanych na rysunku 1:

Języki VSIDLC

Rysunek 1. Języki VSIDLC.

VSIDLC opiera się głównie na języku bufora protokołu (protobuf). Protobuf określa typy danych wymieniane za pomocą wywołań publikowania i subskrybowania oraz zdalnych wywołań procedur (RPC). Z technicznego punktu widzenia modele VSIDL to pliki TextProto, a składnia VSIDL jest zdefiniowana w pliku protobuf (syntax.proto).

Do generowania kodu w Rust używane są zarówno pliki protobuf do określania typów danych, jak i modele VSIDL. Wygenerowany kod zawiera struktury danych wymienianych komunikatów i funkcje Rust do tworzenia jednostek usługi, bez automatycznego wywoływania tych jednostek. Niestandardowy kod Rust używa następnie wygenerowanego kodu do tworzenia instancji jednostek usługi i implementowania logiki aplikacji.

Składnia abstrakcyjna

Rysunek 2 przedstawia podstawowe typy wiadomości w VSIDL:

Podstawowe typy wiadomości w VSIDL

Rysunek 2. Podstawowe typy wiadomości w VSIDL.

Wpis VSIDL

W tej sekcji opisano typ wiadomości wejściowej VSIDL.

Gramatyka EBNF

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

Definicja 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;
}

Przykład zastosowania

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

service_bundle {
  name: "Manager"

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

Wyjaśnienie

Komunikat VsidlEntry służy jako kontener główny pliku VSIDL (z rozszerzeniem .vsidl). Ten komunikat zawiera wszystkie definicje i konfiguracje w jednym pliku VSIDL. VsidlEntry to element najwyższego poziomu, który wiąże wszystkie pozostałe elementy.

Cel:

  • Definiuje ogólną strukturę pliku VSIDL.
  • Określa przestrzeń nazw pakietu dla wszystkich encji w pliku.
  • Zawiera kolekcję definicji pakietów usług.
  • Umożliwia niestandardowe rozszerzenia modelu VSIDL.
  • Obejmuje reguły mapowania dla SOME/IP i VHAL.

Ograniczenia

  • Nazwa pakietu (E211): nazwa pakietu nie może przekraczać 127 znaków.
  • Pliki osierocone (E10B): wszystkie pliki w katalogu muszą być wymienione w grupie plików Android.bp.

Pakiet usług

W tej sekcji opisano typ wiadomości dotyczący pakietu usług.

Gramatyka 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
;

Definicja 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;
}

Przykład zastosowania

service_bundle {
    name: "SeatController"

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

Wyjaśnienie

Pakiet usług definiuje grupę logiczną powiązanych usług, aplikacji publikujących, aplikacji subskrybujących, serwerów RPC i klientów RPC. Pakiet usług działa jako kontener dla określonego zestawu funkcji i ich interakcji.

Ograniczenia

  • Wymagania dotyczące nazwy pakietu (E209, E20A, E20B, E20C):
    • Pakiet usług musi mieć wypełnioną nazwę.
    • Nazwa musi zaczynać się od prawidłowego znaku początkowego identyfikatora Unicode (zwykle litery).
    • Kolejne znaki w nazwie muszą być prawidłowymi znakami kontynuacji identyfikatora Unicode (zwykle literami lub cyframi).
    • Nazwa nie może być zarezerwowanym słowem kluczowym w języku Rust, Java ani C++.
  • Globalna unikalność pakietu (E309): każdy pakiet usług musi mieć unikalną, w pełni kwalifikowaną nazwę (na podstawie pakietu i nazwy).
  • Unikalność wewnętrzna (E100, E300, E302, E303, E308):
    • W ramach jednego pakietu usług każda usługa RPC może być obsługiwana przez maksymalnie 1 definicję serwera.
    • W ramach jednego pakietu usług każdy typ publikacji MULTI_PUB może być publikowany przez maksymalnie 1 definicję wydawcy.
    • W ramach jednego pakietu usług wszystkie nazwy jednostek usług zdefiniowanych przez użytkownika (w przypadku wydawców lub serwerów) muszą być unikalne.
    • W ramach jednego pakietu usług wszystkie nazwy jednostek usług (zdefiniowane przez użytkownika lub wygenerowane automatycznie) muszą być unikalne.
  • Konwencje nazewnictwa celu kompilacji (E205, E206, E207, E208): jeśli podano niestandardową nazwę celu kompilacji (build_cfg.target_name), musi ona być zgodna z formatem snake_case (małe litery, cyfry i pojedyncze podkreślenia, nie zaczynające się ani nie kończące podkreśleniem).
  • Unikalność nazwy celu kompilacji (E301): zdefiniowana przez użytkownika nazwa celu kompilacji nie może kolidować z automatycznie generowanymi nazwami celów dla innych pakietów usług.

Wydawca

W tej sekcji opisano typ wiadomości wydawcy.

Gramatyka EBNF

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

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

Definicja 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;
}

Przykład zastosowania

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

Wyjaśnienie

Typ wiadomości Publisher definiuje źródło danych, które udostępnia ServiceBundle. Ten typ wiadomości określa typ publikowanych danych oraz konkretne tematy i pojemność tych danych.

Tematy

Każda instancja Publisher ma pole message, które odnosi się do publikowanej wiadomości proto. Musi określać temat (reprezentowany przez topic) i pojemność (reprezentowaną przez capacity).

  • Temat: unikalny identyfikator tematu publikacji. Musi być zapisana małymi literami i zawierać łączniki (np. my-topic).
  • Pojemność: określa rozmiar kolejki, czyli liczbę wiadomości, które kolejka może pomieścić, zanim nieprzeczytane wiadomości zostaną odrzucone. Musi to być liczba parzysta większa lub równa 2.

Nazwy zdefiniowane przez użytkownika

Wydawcy mogą mieć zdefiniowane przez użytkownika nazwy jednostek usług, które zastępują automatycznie wybrane nazwy jednostek usług. Ta funkcja umożliwia wybieranie krótszych nazw. Jeśli wydawca używa nazwy jednostki usługi zdefiniowanej przez użytkownika, może używać tylko jednej instancji, tak aby nazwa jednostki usługi była jednoznacznie przypisana do jednej instancji.

# 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"
}

Ograniczenia

  • Miejsce docelowe wydawcy (E300): wydawcy tego samego typu MULTI_PUB muszą być zdefiniowani w osobnych pakietach usług.
  • Unikalność nazwy lokalnej (E302): w ramach jednego pakietu usług wszyscy wydawcy muszą mieć unikalne nazwy jednostek usług zdefiniowane przez użytkownika.
  • Globalna unikalność nazwy (E304): wydawcy tego samego typu publikacji muszą mieć globalnie unikalne nazwy jednostek usług zdefiniowane przez użytkownika we wszystkich pakietach usług.
  • Nazywanie pojedynczych kanałów (E306): nazwy jednostek usług zdefiniowane przez użytkownika można przypisywać tylko do wydawców, którzy obsługują dokładnie 1 instancję.
  • Limit jednego wydawcy (E307): wiadomość protobuf oznaczona jako SINGLE_PUB może zostać opublikowana tylko przez jednego wydawcę w całym systemie.
  • Unikalność nazwy jednostki usługi (E308): wszystkie nazwy jednostek usługi (wygenerowane lub zdefiniowane przez użytkownika) muszą być unikalne w ramach pakietu usług. Nazwy zdefiniowane przez użytkownika powinny być używane do rozwiązywania konfliktów z wygenerowanymi nazwami.
  • Wymaganie dotyczące specyfikacji wariantu (E501): gdy wydawca używa nazwy zdefiniowanej przez użytkownika dla typu z wieloma wariantami, musi wyraźnie określić wariant, który publikuje.
  • Istnienie wydawcy dla subskrybentów (E504): każdy zdefiniowany subskrybent wymaga co najmniej 1 odpowiadającego mu wydawcy dla określonego typu i wariantu.
  • Prawidłowy typ wydawcy (E601): wydawca musi odwoływać się do typu, który odpowiada istniejącej wiadomości protobuf.
  • Wymaganie dotyczące adnotacji publikacji (E602): typ wiadomości protobuf, do którego odwołuje się wydawca, musi zawierać adnotację SdvPublication.
  • Prawidłowe użycie wariantu (E606): jeśli wydawca określi wariant (instancję), musi on istnieć w instances_enum zdefiniowanym dla typu publikacji w protobuf.
  • Warunek specyfikacji wariantu (E607): wydawca może określić konkretny wariant (instancję) tylko wtedy, gdy typ publikacji definiuje instances_enum w protobuf.
  • Nazewnictwo tematów (E20D, E20F): tematy muszą być zapisane małymi literami i rozdzielone myślnikami, a ich długość nie może przekraczać 127 znaków.
  • Unikalność tematów (E314): tematy muszą być unikalne globalnie dla wszystkich wydawców.
  • Wymagania dotyczące pojemności (E406, E407): pojemność jest obowiązkowa i musi być liczbą parzystą >= 2.

Subskrybent

W tej sekcji opisujemy typ wiadomości dla subskrybentów.

Gramatyka EBNF

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

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

Definicja 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;
}

Przykład zastosowania

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

Wyjaśnienie

Komunikat Subscriber definiuje odbiorcę publikacji, którego udostępnia ServiceBundle. Ten komunikat określa typ danych, które są subskrybowane, oraz konkretne tematy tej publikacji. Jeśli dany temat ma wielu wydawców, subskrybent otrzymuje wiadomości opublikowane przez wszystkich z nich.

Ograniczenia

  • Istnienie wydawcy (E504): dla każdego zdefiniowanego subskrybenta musi istnieć co najmniej 1 odpowiadający mu wydawca, który publikuje określony typ i wariant publikacji.
  • Prawidłowy typ subskrypcji (E608): subskrybent musi odwoływać się do typu, który odpowiada istniejącej wiadomości protobuf zdefiniowanej za pomocą adnotacji SdvPublication.
  • Prawidłowa subskrypcja wariantu (E609): jeśli subskrybent określi wariant (instancję), musi on być prawidłową wartością zdefiniowaną w instances_enum odpowiedniego typu publikacji protobuf.
  • Temat jest obowiązkowy (E408): temat jest obowiązkowy dla subskrybentów.
  • Ponowna deklaracja tematu (E311): tematy subskrybenta nie powinny być ponownie deklarowane w tym samym pakiecie usług.

Serwer RPC

W tej sekcji opisano typ komunikatu serwera RPC.

Gramatyka EBNF

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

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

Definicja 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;
}

Przykład zastosowania

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

Wyjaśnienie

Komunikat Server definiuje serwer RPC, który udostępnia ServiceBundle. Ten komunikat określa usługę, którą implementuje serwer, oraz kanał RPC.

Serwer RPC udostępnia zestaw metod, które klienci mogą wywoływać zdalnie. Pole service określa nazwę usługi RPC, którą implementuje serwer. Ta usługa jest zdefiniowana w pliku proto i zaimplementowana w niestandardowym kodzie Rust. Usługi RPC mogą obejmować metody jednoargumentowe, strumieniowe po stronie klienta i strumieniowe po stronie serwera, zgodnie z definicją usługi protobuf. Pole channel definiuje punkt końcowy komunikacji i jest obowiązkowe (E409).

Ograniczenia

  • Definicja usługi (E603): serwer RPC musi określać wartość service odpowiadającą istniejącej wartości service protobuf RPC.
  • Limit serwerów na usługę (E100): w ramach jednego pakietu usług określone wywołanie RPC service może być obsługiwane przez maksymalnie 1 definicję serwera.
  • Nazwy kanałów (E20E): nazwy kanałów RPC muszą być zapisane małymi literami i rozdzielone łącznikami.
  • Channel mandatory (E409): kanał RPC jest obowiązkowy.
  • Unikalność kanału (E40B): kanał RPC musi być używany tylko przez jedną usługę.

Klient RPC

W tej sekcji opisano typ wiadomości klienta RPC.

Gramatyka EBNF

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

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

Definicja 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;
}

Przykład zastosowania

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

Wyjaśnienie

client definiuje klienta RPC, którego używa ServiceBundle. client określa usługę, z którą klient wchodzi w interakcję, oraz kanał, z którym ma się połączyć. Klient może wchodzić w interakcje z metodami jednoargumentowymi, strumieniowymi po stronie klienta i strumieniowymi po stronie serwera, w zależności od definicji usługi.

Ograniczenia

  • Definicja usługi (E60A): klient RPC musi określić service, który odpowiada istniejącej definicji protobuf service.
  • Unikalne źródło usługi (E60B): definicja protobuf service, do której odwołuje się service klienta RPC, musi być zdefiniowana jednoznacznie (nie może być zdefiniowana wielokrotnie) we wszystkich plikach protobuf.
  • Channel mandatory (E409): kanał RPC jest obowiązkowy.

Konfiguracja kompilacji

W tej sekcji opisano typ wiadomości konfiguracji kompilacji.

Gramatyka EBNF

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

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

Definicja 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;
}

Przykład zastosowania

build_cfg {
  target_name: "my_custom_target_name"
  skip_codegen: false
}

Wyjaśnienie

BuildConfiguration konfiguruje niestandardowe parametry ServiceBundle na potrzeby generowania kodu. Wszystkie konfiguracje kompilacji są opcjonalne.

  • target_name (opcjonalnie string): określa nazwę celu kompilacji w plikach Android.bp. Użyj tej opcji, aby ustawić nazwy celów, które są krótsze niż nazwy wybrane automatycznie.
  • skip_codegen (opcjonalnie bool): wskazuje, czy generowanie kodu powinno zostać pominięte w przypadku tego pakietu usług. Jeśli ustawisz wartość true, dla tego pakietu usług nie zostanie wygenerowany żaden kod. Może to być przydatne w przypadku pakietów usług, które są wdrażane ręcznie. Domyślnie jest to false.

Ograniczenia

  • Format nazwy celu (E205, E206, E207, E208): jeśli podano niestandardową nazwę celu kompilacji (build_cfg.target_name), musi ona być sformatowana ściśle według stylu snake_case:
    • Może zawierać tylko małe litery (od a do z), cyfry (od 0 do 9) i podkreślenia (_).
    • Nie może zawierać kolejnych podkreśleń (__).
    • Nie może zaczynać się od podkreślenia.
    • Nie może kończyć się podkreśleniem.
  • Unikalność nazwy celu (E301): zdefiniowany przez użytkownika build_cfg.target_name musi być unikalny w systemie kompilacji i nie może kolidować z automatycznie generowanymi nazwami celów pochodzącymi z innych definicji pakietów usług.