מפרט השפה

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

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

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

שפות VSIDLC

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

ה-VSIDLC מבוסס בעיקר על שפת מאגר אחסון לפרוטוקולים (protobuf). ‫Protobuf משמש לציון סוגי הנתונים שמועברים באמצעות פרסום והרשמה (publish-subscribe) וקריאות לפרוצדורות מרוחקות (RPC). מבחינה טכנית, מודלים של VSIDL הם קובצי TextProto, שבהם התחביר של VSIDL מוגדר בקובץ protobuf ‏(syntax.proto). קובצי ה-protobuf שמשמשים להגדרת סוגי נתונים ומודלים של VSIDL משמשים ליצירת קוד Rust. הקוד שנוצר מכיל בעיקר מבנים שמיישמים את מבני הנתונים של ההודעות המוחלפות ופונקציות Rust שמיישמות פונקציות ליצירת יחידות שירות ב-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 filegroup.

חבילת שירותים

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

דקדוק 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 סוג של פרסום יכול להתפרסם על ידי הגדרת מפרסם אחת לכל היותר.
    • בתוך חבילת שירותים אחת, כל שמות יחידות השירות שהוגדרו על ידי המשתמש (לבעלי אתרים או לשרתים) חייבים להיות ייחודיים.
    • בכל חבילת שירותים, כל שמות יחידות השירות (בין אם הם מוגדרים על ידי המשתמש או נוצרים אוטומטית) חייבים להיות ייחודיים.
  • מוסכמות למתן שמות ליעדי בנייה (E205,‏ E206,‏ E207,‏ E208): אם מציינים שם מותאם אישית של יעד בנייה (build_cfg.target_name), הוא חייב להיות בפורמט snake case (אותיות קטנות, מספרים וקווים תחתונים בודדים, בלי קו תחתון בהתחלה או בסוף).
  • יצירת שם ייחודי ליעד (E301): שם יעד הבנייה שהוגדר על ידי המשתמש לא יכול להיות זהה לשמות של יעדים שנוצרו באופן אוטומטי עבור חבילות שירות אחרות.

מוציא לאור

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

דקדוק 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 שמפנה להודעת הפרוטו שמתפרסמת. חובה לציין נושא (מיוצג על ידי topic) וקיבולת (מיוצגת על ידי capacity).

  • נושא: מזהה ייחודי של נושא הפרסום. הוא חייב להיות בפורמט dash-case באותיות קטנות (לדוגמה, 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,‏ client-streaming ו-server-streaming, כפי שמוגדר בהגדרת שירות ה-protobuf. השדה channel מגדיר את נקודת הקצה של התקשורת והוא שדה חובה (E409).

מגבלות

  • הגדרת שירות (E603): שרת RPC חייב לציין ערך service שמתאים לערך קיים של protobuf RPC service.
  • מגבלת שרת לכל שירות (E100): בחבילת שירותים אחת, אפשר להגדיר לכל היותר שרת אחד ל-RPC service ספציפי.
  • שמות של ערוצים (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,‏ client-streaming ו-server-streaming, בהתאם להגדרת השירות.

מגבלות

  • הגדרת שירות (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 ליצירת קוד. כל הגדרות הבנייה הן אופציונליות.

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

מגבלות

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