مشخصات زبان

این صفحه زبان تعریف رابط سرویس خودرو (VSIDL) را با استفاده از فرم توسعه‌یافته Backus-Naur (EBNF) و protobuf استاندارد ISO/IEC 14977 مشخص می‌کند. این صفحه بر گرامر مستقل از متن زبان و معنای عناصر زبان تمرکز دارد.

سلسله مراتب زبان

طبق گفته‌ی Meta Object Facility (MOF) ، کامپایلر VSIDL (VSIDLC) از زبان‌های نشان داده شده در شکل ۱ استفاده می‌کند:

زبان‌های VSIDLC

شکل 1. زبان‌های VSIDLC.

VSIDLC عمدتاً مبتنی بر زبان بافر پروتکل (protobuf) است. Protobuf برای مشخص کردن انواع داده‌هایی که با استفاده از فراخوانی‌های انتشار-اشتراک و فراخوانی‌های رویه از راه دور (RPC) رد و بدل می‌شوند، استفاده می‌شود. از نظر فنی، مدل‌های VSIDL فایل‌های TextProto هستند که در آن‌ها سینتکس VSIDL در یک فایل protobuf ( syntax.proto ) تعریف شده است. هم فایل‌های protobuf برای تعیین انواع داده‌ها و هم مدل‌های VSIDL برای تولید کد Rust استفاده می‌شوند. کد تولید شده عمدتاً شامل ساختارهایی است که ساختارهای داده را برای پیام‌های رد و بدل شده پیاده‌سازی می‌کنند و توابع Rust که توابعی را برای ایجاد واحدهای سرویس در Rust پیاده‌سازی می‌کنند، بدون اینکه این واحدهای سرویس به طور خودکار فراخوانی شوند. این کد Rust تولید شده با کد Rust سفارشی همراه است که از کد تولید شده برای نمونه‌سازی واحدهای سرویس و پیاده‌سازی منطق تجاری برنامه استفاده می‌کند.

نحو انتزاعی

شکل 2 انواع پیام‌های اصلی در VSIDL را نشان می‌دهد:

انواع پیام‌های اصلی در VSIDL

شکل ۲. انواع پیام‌های اصلی در VSIDL.

ورودی VSIDL

این بخش نوع پیام ورودی VSIDL را توضیح می‌دهد.

دستور زبان EBNF

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

تعریف پروتو

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

مثال استفاده

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

service_bundle {
  name: "Manager"

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

توضیح

پیام VsidlEntry به عنوان ظرف ریشه برای فایل VSIDL (با پسوند .vsidl ) عمل می‌کند. این پیام تمام تعاریف و پیکربندی‌ها را در یک فایل VSIDL واحد کپسوله می‌کند. VsidlEntry عنصر سطح بالایی است که هر چیز دیگری را به هم متصل می‌کند.

هدف:

  • ساختار کلی یک فایل VSIDL را تعریف می‌کند.
  • فضای نام بسته را برای همه موجودیت‌های درون فایل مشخص می‌کند.
  • شامل مجموعه‌ای از تعاریف بسته‌های خدماتی است.
  • امکان افزودن افزونه‌های سفارشی به مدل VSIDL را فراهم می‌کند.
  • شامل قوانین نگاشت برای SOME/IP و VHAL است.

محدودیت‌ها

  • نام بسته (E211): نام بسته نباید بیش از ۱۲۷ کاراکتر باشد.
  • فایل‌های معلق (E10B): تمام فایل‌های موجود در یک کاتالوگ باید در یک گروه فایل Android.bp ارجاع داده شوند.

بسته خدماتی

این بخش نوع پیام بسته خدماتی را توضیح می‌دهد.

دستور زبان 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
;

تعریف پروتو

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

مثال استفاده

service_bundle {
    name: "SeatController"

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

توضیح

یک بسته سرویس، گروه‌بندی منطقی سرویس‌های مرتبط، ناشران، مشترکین، سرورهای RPC و کلاینت‌های RPC را تعریف می‌کند. یک بسته سرویس به عنوان ظرفی برای مجموعه‌ای خاص از قابلیت‌ها و تعاملات آنها عمل می‌کند.

محدودیت‌ها

  • الزامات نام بسته (E209، E20A، E20B، E20C):
    • یک بسته خدماتی باید یک نام مشخص داشته باشد.
    • نام باید با یک کاراکتر شروع معتبر یونیکد (معمولاً یک حرف) شروع شود.
    • کاراکترهای بعدی در نام باید کاراکترهای ادامه دهنده شناسه یونیکد معتبر باشند (معمولاً حروف یا اعداد).
    • این نام نباید یک کلمه کلیدی رزرو شده در Rust، Java یا C++ باشد.
  • منحصر به فرد بودن بسته‌ی سرویس (E309): هر بسته‌ی سرویس باید یک نام کاملاً منحصر به فرد (بر اساس بسته و نام آن) داشته باشد.
  • منحصر به فرد بودن داخلی (E100، E300، E302، E303، E308):
    • در یک بسته سرویس واحد، هر سرویس RPC می‌تواند حداکثر توسط یک تعریف سرور ارائه شود.
    • در یک بسته سرویس واحد، هر نوع انتشار MULTI_PUB می‌تواند حداکثر با یک تعریف ناشر منتشر شود.
    • در یک بسته سرویس واحد، تمام نام‌های واحد سرویس تعریف‌شده توسط کاربر (برای ناشران یا سرورها) باید منحصر به فرد باشند.
    • در یک بسته سرویس واحد، تمام نام‌های واحدهای سرویس (چه تعریف‌شده توسط کاربر و چه به‌صورت خودکار ایجاد شده) باید منحصربه‌فرد باشند.
  • قراردادهای نامگذاری هدف ساخت (E205، E206، E207، E208): اگر یک نام هدف ساخت سفارشی ( build_cfg.target_name ) ارائه شود، باید از قالب حروف کوچک و بزرگ (حروف کوچک، اعداد و زیرخط‌های تکی، بدون شروع یا پایان با زیرخط) پیروی کند.
  • منحصر به فرد بودن نام هدف ساخت (E301): نام هدف ساخت تعریف شده توسط کاربر نباید با هیچ نام هدفی که به طور خودکار برای سایر بسته‌های خدماتی ایجاد می‌شود، تداخل داشته باشد.

ناشر

این بخش نوع پیام ناشر را توضیح می‌دهد.

دستور زبان EBNF

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

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

تعریف پروتو

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

مثال استفاده

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

توضیح

نوع پیام Publisher منبع داده‌ای را که ServiceBundle ارائه می‌دهد، تعریف می‌کند. این نوع پیام، نوع داده‌ای که منتشر می‌شود و موضوعات خاص و ظرفیت آن داده را مشخص می‌کند.

مباحث

هر نمونه Publisher یک فیلد message دارد که به پیام اولیه‌ای که منتشر می‌شود اشاره دارد. این فیلد باید یک موضوع (که با topic نشان داده می‌شود) و یک ظرفیت (که با capacity نشان داده می‌شود) را مشخص کند.

  • موضوع: یک شناسه منحصر به فرد برای موضوع انتشار. باید از خط تیره کوچک (مثلاً my-topic ) پیروی کند.
  • ظرفیت: اندازه صف را مشخص می‌کند، یعنی اینکه صف می‌تواند قبل از حذف پیام‌های خوانده نشده، چند پیام را در خود جای دهد. این مقدار باید زوج و بزرگتر یا مساوی ۲ باشد.

نام‌های تعریف‌شده توسط کاربر

ناشران می‌توانند نام‌های واحد خدمات تعریف‌شده توسط کاربر داشته باشند که نام‌های واحد خدمات انتخاب‌شده خودکار را نادیده می‌گیرند. این قابلیت، انتخاب نام‌های کوتاه‌تر را امکان‌پذیر می‌کند. اگر ناشری از نام واحد خدمات تعریف‌شده توسط کاربر استفاده کند، ممکن است فقط از یک نمونه استفاده کند، به طوری که نام واحد خدمات به طور منحصر به فرد به یک نمونه اختصاص داده شود.

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

محدودیت‌ها

  • جایگذاری ناشر (E300): ناشران برای نوع MULTI_PUB یکسان باید در بسته‌های سرویس جداگانه تعریف شوند.
  • منحصر به فرد بودن نام محلی (E302): در هر بسته سرویس واحد، همه ناشران باید نام‌های واحد سرویس تعریف شده توسط کاربر منحصر به فرد داشته باشند.
  • منحصر به فرد بودن نام جهانی (E304): ناشران از یک نوع انتشار باید نام‌های واحد خدمات تعریف شده توسط کاربر را به صورت جهانی و منحصر به فرد در تمام بسته‌های خدمات داشته باشند.
  • نامگذاری کانال‌های تکی (E306): نام‌های واحد سرویس تعریف‌شده توسط کاربر را می‌توان فقط به ناشرانی اختصاص داد که دقیقاً یک نمونه را مدیریت می‌کنند.
  • محدودیت انتشار تکی (E307): یک پیام protobuf که با عنوان SINGLE_PUB مشخص شده است، فقط می‌تواند توسط یک ناشر در کل سیستم منتشر شود.
  • منحصر به فرد بودن نام واحد خدمات (E308): تمام نام‌های واحد خدمات (چه تولید شده و چه تعریف شده توسط کاربر) باید در بسته خدمات خود منحصر به فرد باشند؛ برای رفع تداخل با نام‌های تولید شده، باید از نام‌های تعریف شده توسط کاربر استفاده شود.
  • الزام مشخصات متغیر (E501): هنگامی که ناشر از نام تعریف‌شده توسط کاربر برای نوعی با چندین متغیر استفاده می‌کند، باید صریحاً متغیری را که منتشر می‌کند، مشخص کند.
  • وجود ناشر برای مشترکین (E504): هر مشترک تعریف‌شده برای نوع و گونه‌ی مشخص‌شده، حداقل به یک ناشر مربوطه نیاز دارد.
  • نوع ناشر معتبر (E601): یک ناشر باید به نوعی اشاره کند که با یک پیام protobuf موجود مطابقت داشته باشد.
  • الزام حاشیه‌نویسی انتشار (E602): نوع پیام protobuf که توسط ناشر ارجاع داده می‌شود باید شامل حاشیه‌نویسی SdvPublication باشد.
  • استفاده‌ی معتبر از نوع داده (E606): اگر ناشری یک نوع داده (نمونه) را مشخص کند، آن نوع داده باید در instances_enum تعریف شده برای نوع انتشار در protobuf وجود داشته باشد.
  • شرط تعیین نوع (E607): یک ناشر می‌تواند یک نوع (نمونه) صریح را تنها در صورتی مشخص کند که نوع انتشار، instances_enum را در protobuf تعریف کند.
  • نامگذاری موضوعات (E20D، E20F): موضوعات باید با حروف کوچک و خط تیره نوشته شوند و بیش از ۱۲۷ کاراکتر نباشند.
  • منحصر به فرد بودن موضوع (E314): موضوعات باید در سطح جهانی در بین همه ناشران منحصر به فرد باشند.
  • الزامات ظرفیت (E406، E407): ظرفیت اجباری است و باید عددی زوج >= 2 باشد.

مشترک

این بخش نوع پیام مشترک را توضیح می‌دهد.

دستور زبان EBNF

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

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

تعریف پروتو

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

مثال استفاده

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

توضیح

پیام Subscriber ، یک گیرنده انتشار را که ServiceBundle ارائه می‌دهد، تعریف می‌کند. این پیام نوع داده‌ای که در آن مشترک می‌شود و موضوعات خاص آن انتشار را مشخص می‌کند. اگر چندین ناشر برای یک موضوع وجود داشته باشد، مشترک پیام‌های منتشر شده توسط همه آنها را دریافت می‌کند.

محدودیت‌ها

  • وجود ناشر (E504): برای هر مشترک تعریف‌شده، حداقل یک ناشر مربوطه باید وجود داشته باشد که نوع و گونه‌ی انتشار مشخص‌شده را منتشر کند.
  • نوع اشتراک معتبر (E608): یک مشترک باید به نوعی اشاره کند که با یک پیام protobuf موجود که با حاشیه‌نویسی SdvPublication تعریف شده است، مطابقت داشته باشد.
  • اشتراک معتبر نوع داده (E609): اگر مشترکی یک نوع داده (نمونه) را مشخص کند، آن نوع داده باید یک مقدار معتبر باشد که درون instances_enum از نوع انتشار protobuf مربوطه تعریف شده باشد.
  • اجباری بودن موضوع (E408): موضوع برای مشترکین اجباری است.
  • اعلان مجدد موضوع (E311): موضوعات مشترکین نباید در یک بسته سرویس یکسان دوباره اعلان شوند.

سرور RPC

این بخش نوع پیام سرور RPC را توضیح می‌دهد.

دستور زبان EBNF

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

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

تعریف پروتو

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

مثال استفاده

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

توضیح

پیام Server یک سرور RPC را که ServiceBundle ارائه می‌دهد، تعریف می‌کند. این پیام سرویسی را که سرور پیاده‌سازی می‌کند و کانال RPC را مشخص می‌کند.

یک سرور RPC مجموعه‌ای از متدها را در اختیار کلاینت‌ها قرار می‌دهد که می‌توانند از راه دور فراخوانی شوند. فیلد service ، نام سرویس RPC که سرور پیاده‌سازی می‌کند را مشخص می‌کند. این سرویس در یک فایل proto تعریف شده و با کد سفارشی Rust پیاده‌سازی می‌شود. سرویس‌های RPC می‌توانند شامل متدهای unary، client-streaming و server-streaming باشند، همانطور که در تعریف سرویس protobuf تعریف شده است. فیلد channel ، نقطه پایانی ارتباط را تعریف می‌کند و اجباری است (E409).

محدودیت‌ها

  • تعریف سرویس (E603): یک سرور RPC باید مقدار service را مشخص کند که با مقدار service موجود در protobuf RPC مطابقت داشته باشد.
  • محدودیت سرور به ازای هر سرویس (E100): در یک بسته سرویس واحد، یک service RPC خاص می‌تواند حداکثر توسط یک تعریف سرور ارائه شود.
  • نامگذاری کانال (E20E): کانال‌های RPC باید با حروف کوچک و خط تیره نوشته شوند.
  • کانال اجباری (E409): کانال RPC اجباری است.
  • منحصر به فرد بودن کانال (E40B): کانال RPC باید فقط توسط یک سرویس استفاده شود.

کلاینت RPC

این بخش نوع پیام کلاینت RPC را توضیح می‌دهد.

دستور زبان EBNF

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

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

تعریف پروتو

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

مثال استفاده

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

توضیح

client یک کلاینت RPC تعریف می‌کند که ServiceBundle از آن استفاده می‌کند. client سرویسی را که کلاینت با آن تعامل دارد و کانالی را که باید به آن متصل شود، مشخص می‌کند. کلاینت می‌تواند بسته به تعریف سرویس، با متدهای unary، client-streaming و server-streaming تعامل داشته باشد.

محدودیت‌ها

  • تعریف سرویس (E60A): یک کلاینت RPC باید service مشخص کند که با تعریف service موجود در protobuf مطابقت داشته باشد.
  • منبع سرویس منحصر به فرد (E60B): تعریف service protobuf که توسط service کلاینت RPC به آن ارجاع داده می‌شود، باید به صورت منحصر به فرد (نه چندین بار تعریف شده) در تمام فایل‌های protobuf تعریف شود.
  • کانال اجباری (E409): کانال RPC اجباری است.

پیکربندی ساخت

این بخش نوع پیام پیکربندی Build را توضیح می‌دهد.

دستور زبان EBNF

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

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

تعریف پروتو

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

مثال استفاده

build_cfg {
  target_name: "my_custom_target_name"
  skip_codegen: false
}

توضیح

BuildConfiguration پارامترهای غیر استاندارد ServiceBundle را برای تولید کد پیکربندی می‌کند. همه پیکربندی‌های ساخت اختیاری هستند.

  • target_name ( string اختیاری): نام هدف ساخت را در فایل‌های Android.bp مشخص می‌کند. از این برای تنظیم نام‌های هدف با نام‌های کوتاه‌تر از نام‌های انتخاب‌شده خودکار استفاده کنید.
  • skip_codegen (اختیاری bool ): نشان می‌دهد که آیا تولید کد باید برای این بسته سرویس نادیده گرفته شود یا خیر. اگر روی true تنظیم شود، هیچ کدی برای این بسته سرویس خاص تولید نمی‌شود. این می‌تواند برای بسته‌های سرویسی که به صورت دستی پیاده‌سازی می‌شوند مفید باشد. به طور پیش‌فرض، این مقدار روی false تنظیم شده است.

محدودیت‌ها

  • قالب نام هدف (E205، E206، E207، E208): اگر یک نام هدف ساخت سفارشی ( build_cfg.target_name ) ارائه شود، باید دقیقاً از قالب‌بندی حروف کوچک و بزرگ پیروی کند:
    • فقط باید شامل حروف کوچک ( a تا z )، اعداد ( 0 تا 9 ) و زیرخط ( _ ) باشد.
    • نباید شامل زیرخط‌های متوالی ( __ ) باشد.
    • نباید با زیرخط (_) شروع شود.
    • نباید با زیرخط (_) تمام شود.
  • منحصر به فرد بودن نام هدف (E301): یک build_cfg.target_name تعریف شده توسط کاربر باید در سراسر سیستم ساخت منحصر به فرد باشد و نباید با هیچ نام هدفی که به طور خودکار از سایر تعاریف بسته‌های خدماتی تولید می‌شود، تداخل داشته باشد.