מפרט השפה

בדף הזה מפורטת שפת ההגדרה של ממשק שירות הרכב (VSIDL) באמצעות ISO/IEC 14977 Extended Backus-Naur Form‏ (EBNF) ו-protobuf. בדף הזה נתמקד בדקדוק חופשי-הקשר של השפה ובמשמעות של רכיבי השפה.

היררכיית השפות

בהתאם ל-Meta Object Facility (MOF), המהדר של VSIDL‏ (VSIDLC) משתמש בשפות שמוצגות באיור 1:

שפות VSIDLC

איור 1. שפות VSIDLC.

‫VSIDLC מבוסס בעיקר על שפת מאגר האחסון לפרוטוקולים (protobuf). ‫Protobuf מציין את סוגי הנתונים שמוחלפים באמצעות פרסום-מינוי וקריאות לשירות מרוחק (RPC). מבחינה טכנית, מודלים של VSIDL הם קובצי TextProto, והתחביר של VSIDL מוגדר בקובץ protobuf‏ (syntax.proto).

גם קובצי ה-protobuf לציון סוגי נתונים וגם המודלים של VSIDL משמשים ליצירת קוד Rust. הקוד שנוצר מכיל מבנים של נתוני ההודעות שמועברות ופונקציות Rust ליצירת יחידות שירות, בלי לקרוא ליחידות השירות האלה באופן אוטומטי. לאחר מכן, קוד Rust בהתאמה אישית משתמש בקוד שנוצר כדי ליצור מופעים של יחידות שירות ולהטמיע את הלוגיקה של האפליקציה.

תחביר מופשט

באיור 2 מוצגים סוגי ההודעות העיקריים ב-VSIDL:

סוגי ההודעות העיקריים ב-VSIDL

איור 2. סוגי ההודעות העיקריים ב-VSIDL.

רשומת VSIDL

בקטע הזה מוסבר על סוג ההודעה של רשומת VSIDL.

דקדוק EBNF

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

הגדרת 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;
}

דוגמה לשימוש

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): שם החבילה לא יכול להיות ארוך מ-127 תווים.
  • קבצים מיותרים (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
;

הגדרת 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;
}

דוגמה לשימוש

service_bundle {
    name: "SeatController"

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

הסבר

חבילת שירותים מגדירה קיבוץ לוגי של שירותים קשורים, בעלי תוכן דיגיטלי, מנויים, שרתי RPC ולקוחות RPC. חבילת שירותים פועלת כמאגר של קבוצת פונקציות ספציפית והאינטראקציות שלהן.

מגבלות

  • דרישות לגבי שם החבילה (E209, ‏ E20A, ‏ E20B, ‏ E20C):
    • חבילת שירותים חייבת לכלול שם.
    • השם חייב להתחיל בתו חוקי של מזהה Unicode (בדרך כלל אות).
    • התווים הבאים בשם חייבים להיות תווים חוקיים להמשך של מזהה Unicode (בדרך כלל אותיות או מספרים).
    • השם לא יכול להיות מילת מפתח שמורה ב-Rust, ב-Java או ב-C++‎.
  • ייחודיות גלובלית של חבילות (E309): לכל חבילת שירות חייב להיות שם ייחודי מלא (על סמך החבילה והשם שלה).
  • ייחודיות פנימית (E100, ‏ E300, ‏ E302, ‏ E303, ‏ E308):
    • בתוך חבילת שירות אחת, כל שירות RPC יכול להיות מוגש על ידי הגדרת שרת אחת לכל היותר.
    • בתוך חבילת שירות אחת, כל סוג פרסום של MULTI_PUB יכול להתפרסם על ידי הגדרת מוציא לאור אחת לכל היותר.
    • בתוך חבילת שירות אחת, כל השמות של יחידות השירות שמוגדרים על ידי המשתמש (לבעלי תוכן דיגיטלי או לשרתים) חייבים להיות ייחודיים.
    • בתוך חבילת שירות אחת, כל השמות של יחידות השירות (בין אם הם מוגדרים על ידי המשתמש או נוצרים באופן אוטומטי) חייבים להיות ייחודיים.
  • מוסכמות למתן שמות ליעדי build‏ (E205,‏ E206,‏ E207,‏ E208): אם מציינים שם מותאם אישית ליעד build‏ (build_cfg.target_name), הוא חייב להיות בפורמט של קו תחתון בין המילים (אותיות קטנות, מספרים וקווים תחתונים בודדים, בלי קו תחתון בהתחלה או בסוף).
  • ייחודיות של שם יעד ה-build‏ (E301): שם יעד ה-build שהמשתמש מגדיר לא יכול להתנגש עם שמות יעדים שנוצרו באופן אוטומטי לחבילות שירות אחרות.

מוציא לאור

בקטע הזה מוסבר על סוג ההודעה "בעל תוכן דיגיטלי".

דקדוק EBNF

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

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

הגדרת 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;
}

דוגמה לשימוש

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

הסבר

סוג ההודעה Publisher מגדיר מקור נתונים ש-ServiceBundle מספק. סוג ההודעה הזה מציין את סוג הנתונים שמתפרסמים, את הנושאים הספציפיים ואת הקיבולת של הנתונים האלה.

נושאים

לכל מופע של Publisher יש שדה message שמפנה להודעת ה-proto שפורסמה. הוא צריך לציין נושא (מיוצג על ידי topic) וקיבולת (מיוצגת על ידי capacity).

  • נושא: מזהה ייחודי של נושא הפרסום. הוא צריך להיות בפורמט של אותיות קטנות עם מקפים (לדוגמה, my-topic).
  • קיבולת: מציינת את גודל התור, כלומר כמה הודעות התור יכול להכיל לפני שהודעות שלא נקראו נמחקות. הערך חייב להיות מספר זוגי מ-2 ומעלה.

שמות שמוגדרים על ידי המשתמש

בעלי תוכן דיגיטלי יכולים להגדיר שמות ליחידות השירות, והשמות האלה יחליפו את השמות שנבחרו באופן אוטומטי. האפשרות הזו מאפשרת לבחור שמות קצרים יותר. אם בעל האפליקציה משתמש בשם של יחידת שירות שהוגדר על ידי המשתמש, הוא יכול להשתמש רק במופע אחד, כך שהשם של יחידת השירות מוקצה באופן ייחודי למופע אחד.

# 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): שמות הנושאים צריכים להיות באותיות קטנות, עם מקפים, ובאורך של עד 127 תווים.
  • ייחודיות של נושאים (E314): הנושאים חייבים להיות ייחודיים באופן גלובלי לכל בעלי התוכן הדיגיטלי.
  • דרישות לגבי קיבולת (E406,‏ E407): חובה לציין קיבולת, והיא חייבת להיות מספר זוגי שגדול מ-2 או שווה לו.

מנויים

בקטע הזה מוסבר על סוג ההודעה "מנויים".

דקדוק EBNF

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

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

הגדרת 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;
}

דוגמה לשימוש

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
;

הגדרת 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;
}

דוגמה לשימוש

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

הסבר

ההודעה Server מגדירה שרת RPC ש-ServiceBundle מספק. ההודעה הזו מציינת את השירות שהשרת מטמיע ואת ערוץ ה-RPC.

שרת RPC חושף קבוצה של שיטות שלקוחות יכולים להפעיל מרחוק. השדה service מציין את שם שירות ה-RPC שהשרת מטמיע. השירות הזה מוגדר בקובץ ‎.proto ומוטמע בקוד Rust בהתאמה אישית. שירותי RPC יכולים לכלול שיטות של unary, סטרימינג של לקוח וסטרימינג של שרת, כפי שמוגדר בהגדרת השירות של 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
;

הגדרת 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;
}

דוגמה לשימוש

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

הסבר

‫client מגדיר לקוח RPC ש-ServiceBundle צורך. client מציין את השירות שהלקוח יוצר איתו אינטראקציה ואת הערוץ שאליו צריך להתחבר. הלקוח יכול לקיים אינטראקציה עם שיטות של Unary, סטרימינג בצד הלקוח וסטרימינג בצד השרת, בהתאם להגדרת השירות.

מגבלות

  • הגדרת שירות (E60A): לקוח RPC חייב לציין service שתואם להגדרה קיימת של protobuf service.
  • מקור שירות ייחודי (E60B): ההגדרה של protobuf service שאליה מפנה service של לקוח RPC חייבת להיות מוגדרת באופן ייחודי (לא מוגדרת כמה פעמים) בכל קובצי ה-protobuf.
  • חובה לציין ערוץ (E409): חובה לציין ערוץ RPC.

תצורת build

בקטע הזה מוסבר סוג ההודעה "הגדרת ה-build".

דקדוק EBNF

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

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

הגדרת 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;
}

דוגמה לשימוש

build_cfg {
  target_name: "my_custom_target_name"
  skip_codegen: false
}

הסבר

BuildConfiguration מגדיר פרמטרים לא סטנדרטיים של ServiceBundle ליצירת קוד. כל תצורות ה-build הן אופציונליות.

  • ‫target_name (אופציונלי string): מציין את השם של יעד ה-build בקובצי Android.bp. אפשר להשתמש באפשרות הזו כדי להגדיר שמות קצרים יותר ליעדים, במקום השמות שנבחרים באופן אוטומטי.
  • ‫skip_codegen (אופציונלי bool): מציין אם לדלג על יצירת הקוד של חבילת השירות הזו. אם הערך הוא true, לא נוצר קוד לחבילת השירות הספציפית הזו. האפשרות הזו יכולה להיות שימושית לחבילות שירות שמוטמעות באופן ידני. כברירת מחדל, ההגדרה היא false.

מגבלות

  • פורמט שם היעד (E205, ‏ E206, ‏ E207, ‏ E208): אם צוין שם יעד בהתאמה אישית (build_cfg.target_name), הוא חייב להיות בפורמט של קו תחתון בין המילים:
    • הוא יכול להכיל רק אותיות קטנות (a עד z), מספרים (0 עד 9) וקווים תחתונים (_).
    • הוא לא יכול להכיל קווים תחתונים רצופים (__).
    • הוא לא יכול להתחיל בקו תחתון.
    • הוא לא יכול להסתיים בקו תחתון.
  • ייחודיות של שם היעד (E301): build_cfg.target_name שמוגדר על ידי המשתמש חייב להיות ייחודי במערכת ה-build, ולא יכול להתנגש עם שמות יעדים שנוצרו באופן אוטומטי ונגזרו מהגדרות אחרות של חבילות שירות.