הגדרת כלי התזמור

‫Orchestrator הוא סוכן SDV מקומי שפועל בכל מכונה וירטואלית (VM) ומספק מנגנון לשליטה על מועד היצירה, ההפעלה, ההפסקה או ההשמדה של חבילות שירות. הפעולה הזו מתבצעת באמצעות הגדרת תזמור, שבה מגדירים קבוצה של כללים שקובעים מתי ואיך יתבצעו פעולות על מופעים של חבילות שירותים. הכללים האלה מבוססים על מצבים של הרכב, מצבי הפעלה ומצבים מותאמים אישית.

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

תרשים של הגדרות מבוזרות של Orchestrator

איור 1. דיאגרמת הגדרת Orchestrator.

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

הגדרות APEX

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

הגדרה לכל מכונה וירטואלית

כשהכלי לניהול סוכנים מופעל, הוא טוען ומנתח את הגדרות ה-VM (אם הן קיימות). הנתיב המוחלט לקובץ ההגדרות הזה מצוין באמצעות מאפייני המערכת persist.sdv.orchestrator_config_path ו-ro.boot.sdv.orchestrator_config_path.

המערכת קובעת את הנתיב לקובץ התצורה של המכונה הווירטואלית בזמן האתחול על סמך ההיררכיה הבאה:

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

  2. אם persist.sdv.orchestrator_config_path ריק, המערכת בודקת את הנכס ro.boot.sdv.orchestrator_config_path. אם למאפיין ro.boot.sdv יש ערך, הנתיב הזה מועתק למאפיין persist ומשמש לאתחול הנוכחי ולכל האתחולים העתידיים (אלא אם הוא מוחלף).

persist.sdv.orchestrator_config_path

‫persist.sdv.orchestrator_config_path הוא המאפיין הראשי שבו סוכן SDV Orchestrator משתמש כדי לקבל את הנתיב לקובץ ההגדרות שלו. זהו מאפיין קבוע, כלומר הערך שלו נשמר גם אחרי הפעלה מחדש של המכשיר. אפשר לשנות את הערך בזמן הריצה, וזה שימושי לבדיקות או לתרחישים ספציפיים (לדוגמה, בדיקות מקצה לקצה).

אפשר להגדיר את הערך בזמן הריצה באמצעות הפקודה setprop, ובמשך זמן של תהליך build באמצעות קובץ makefile (עם הסיומת .mk) או קובץ סקריפט של משאבים, עם הסיומת .rc.

הגדרת המאפיין בזמן הריצה

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

adb root
adb shell setprop persist.sdv.orchestrator_config_path {$path_to_file}.textproto

הגדרת המאפיין בזמן הבנייה

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

# Add this line to a product's or device's .mk file
PRODUCT_PROPERTY_OVERRIDES += persist.sdv.orchestrator_config_path={$path_to_file}.textproto

אפשר גם להגדיר את המאפיין הזה בקובץ סקריפט של משאב:

# Add this line to an .rc file
on {$property}
    setprop persist.sdv.orchestrator_config_path {$path_to_file}.textproto

ro.boot.sdv.orchestrator_config_path

‫ro.boot.sdv.orchestrator_config_path הוא מאפיין לקריאה בלבד בזמן האתחול, שמשמש כדי לספק ערך ראשוני למאפיין persist.sdv.orchestrator_config_path. אם השדה persist.sdv.orchestrator_config_path ריק כשהמערכת מופעלת, הערך של ro.boot.sdv.orchestrator_config_path מועתק אליו. אחרי שמגדירים את persist.sdv.orchestrator_config_path, המאפיין הזה לא יחליף אותו באתחולים הבאים.

אפשר להגדיר את ro.boot.sdv.orchestrator_config_path באמצעות bootconfig או באמצעות שורת הפקודה של ליבת המערכת.

פורמט קובץ

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

תחביר ההגדרות

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

חבילות שירות

כל חבילת שירותים צריכה להיות מוגדרת באמצעות הגדרת חבילת השירותים, שמזהה את החבילה ב-Orchestrator ומשמשת לניהול מחזור החיים של מופע החבילה. בהגדרת חבילת השירות מוגדרים:

  • ‫InstanceToGroupMapping מאפשר לכם לכלול מופע של חבילת השירות בקבוצה כדי ליצור תלות בין מופעים של אותה חבילת שירות.

  • ‫InstancesStates מגדיר את המצבים השונים של מופע חבילת השירותים.

  • השדה InstancesStateConfiguration מגדיר את המצב (מתוך InstancesStates) שבו צריך להגדיר מופע של חבילת שירותים אם התנאי מקבל את הערך true.

  • ServiceBundleConfig מכיל מידע על חבילת השירותים הספציפית ועל המקרים שלה. היא מכילה, עבור החבילה, את הערכים המתאימים של InstanceToGroupMapping ו-InstancesStateConfiguration.

  • ‫CustomModes מגדיר רשימה של מצבים מותאמים אישית שהחבילה יכולה לפרסם. התג CustomModes משמש למניעת שינוי הערך של מצב מותאם אישית על ידי חבילה לא מורשית. השדה הזה הוא אופציונלי, כי יכול להיות שחבילת השירותים לא תפורסם באף מצב מותאם אישית. מידע נוסף זמין במאמר בנושא מצבים מותאמים אישית.

הגדרת חבילה נדרשת

לפחות המאפיינים הבאים מוגדרים בחבילה:

service_bundle_config {
    package_name: "package_name"
    service_bundle_name: "service_bundle_name"

    instance: "instance_1"
    instance: "instance_n"
}

בהצהרה הזו מוגדרים מופעים של חבילות שירות n עם ה-FQIN המתאים:

vm_name.package_name.service_bundle_name.instance_1
…
vm_name.package_name.service_bundle_name.instance_n

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

הגדרת מופעים

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

  • ‫condition הוא ביטוי במצב של המכונה הווירטואלית או הרכב שצריך לבצע בו הערכה.

  • ‫instances_states הוא קבוצה של מצבים לכל מופע שצריך להחיל אם התנאי מקבל את הערך true.

מידע נוסף זמין במאמר בנושא תנאים.

service_bundle_config {
    package_name: "oem.package"
    service_bundle_name: "OemApplication"

    instance: "adaptive_light"
    instance: "reserve_light"

    state {
        condition {
            power_state: "ON"
        }

        instances_states {
            started: "adaptive_light"
            created: "reserve_light"
        }
    }
}

מיפוי מופעים לקבוצות

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

service_bundle_config {
    package_name: "oem.package"
    service_bundle_name: "OemApplication"

    instance: "fog_front_light"
    instance: "fog_rear_light"
    instance: "turn_signal_light"
    instance: "light_flasher_display"

    # Declare that fog_light contains fog_front_light and fog_rear_light.
    group_mapping {
        group: "fog_light"
        instance: "fog_front_light"
        instance: "fog_rear_light"
    }

    # Declare that flasher_light contains turn_signal_light and light_flasher_display.
    group_mapping {
        group: "flasher_light"
        instance: "turn_signal_light"
        instance: "light_flasher_display"
    }
}

סכמת Proto

דוגמה לסכימת proto:

// Service bundle configuration.
//
// Defines service bundle data, its instances and configuration for instances
// states depending on the state of the system
message ServiceBundleConfig {
  // Required. Name of the service bundle.
  string service_bundle_name = 1;

  // Required. Package name of the service bundle.
  string package_name = 2;

  // Required. Service instances.
  repeated string instance = 3;

  // Configuration for instances states depending on the state of the system.
  repeated InstancesStateConfiguration state = 4;

  // Mapping of groups to their member service instances.
  repeated InstanceToGroupMapping group_mapping = 5;

  // Custom modes that this service bundle is allowed to set.
  repeated string custom_mode = 6;

  // Defines the retry policies for specific instances.
  // If multiple mappings target the same instance, the one with the highest `max_retries`
  // value takes precedence. This applies across all configuration files.
  repeated InstanceToRetryMapping retry_mapping = 7;
}

// Mapping of instances to their retry configuration.
message InstanceToRetryMapping {
  // Required.
  //
  // Name of the instances for which the given retry configuration is applied.
  repeated string instance = 1;

  // Required.
  //
  // The configuration that defines the restart and retry strategy for the instances.
  RetryConfiguration retry_config = 2;

  // Configuration for retry and restart.
  // This configuration is applied after a failure on a transition or after the bundle instance
  // has crashed. Upon a successful operation, the retry counter are reset to max_retries. This
  // configuration can be applied to any service bundle, not only the monitored ones. If the
  // configuration is not provided or none of the optional fields are filled, the default behavior
  // stated is applied (the value from `ro.boot.sdv.orchestrator.recovery.max_retries`
  // or zero if not set).
  message RetryConfiguration {
    // The number of times a retry/restart operation can be performed.
    // Defines the number of times the Orchestrator retries a transition
    // after a transient failure or after a bundle crash notification.
    // This applies to creating, starting and destroying operations.
    // The retry count resets to max_retries after a successful operation.
    // If not set, the default configured value in the
    // `ro.boot.sdv.orchestrator.recovery.max_retries` is used, or-if not set-
    // it fallbacks to zero.
    optional uint32 max_retries = 1;
  }
}

// Mapping of groups to their member service instances.
message InstanceToGroupMapping {
  // Required. Names of groups to which members are added.
  //
  // Group behavior is defined in VM configuration.
  repeated string group = 1;

  // Required. Names of instances to be included in the groups.
  //
  // Can reference only instance defined in the same config file.
  repeated string instance = 2;
}

// Describes the state the service instances should be in after the state is executed.
//
// If there is no valid configuration for the instance in the specific system state, such instance is transitioned to the "destroyed" state.
//
// For the GroupsStates definition to be useful, at least one item should be present in any of the fields.
message InstancesStates {
  // Names of the instances that must be in a "created" state.
  repeated string created = 1;

  // Names of the instances that must be in a "started" state, overrides "created" state.
  repeated string started = 2;

  // Names of the instances that must not run, overrides all other states.
  repeated string destroyed = 3;
}

// Configuration for instances states depending on the state of the system.
message InstancesStateConfiguration {
  // Condition for the system state under which the related instances states should be executed by Orchestrator.
  //
  // If omitted, the related instances states are always executed.
  Condition condition = 1;

  // Required. States of service bundle instances to be executed by Orchestrator if the condition is true.
  InstancesStates instances_states = 2;
}

הגדרה ברמת ה-VM

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

ה-Orchestrator לא מבטיח את הסדר שבו מתבצע שינוי המצב. ה-Orchestrator מעביר כל מופע למצב הנתון.

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

מיפוי קבוצה לקבוצה

קבוצות יכולות להכיל קבוצות אחרות. ההצהרה ש-group_1 מכיל את subgroup_2 מוסיפה למעשה את כל מופעי השירות מ-subgroup_2 אל group_1.

לדוגמה:

# Declare that body contains fog_light and flasher_light.
group_mapping {
    group: "body"
    subgroup: "fog_light"
    subgroup: "flasher_light"
}

הגדרת קבוצה

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

אפשר להגדיר קבוצות באופן דומה להגדרת מופעי שירות, כלומר באמצעות מצבי הגדרה. ההבדל היחיד הוא השימוש ב-groups_states במקום ב-instances_states בתחביר:

state {
    condition {
        power_state: "ON"
    }

    groups_states {
        started: "Body"
        started: "Adas"
    }
}

סכמת Proto

הנה סכימת proto לדוגמה:

// VM configuration.
//
// Defines group-to-group mappings and configuration for groups
// states depending on the state of the system.
//
// Configurations of service bundles can also be defined in VM configuration (as well as in a separate configuration file).
message VmConfig {
  // Group to member groups mapping.
  repeated GroupToGroupMapping group_mapping = 1;

  // Configuration of group states.
  repeated GroupsStateConfiguration state = 2;

  // Required. We also allow to configure individual service bundles in the VM config, to simplify development and migration from the monolithic configuration.
  repeated ServiceBundleConfig service_bundle_config = 3;
}

// Mapping of groups to their member groups.
message GroupToGroupMapping {
  // Required. Names of groups to which members are added.
  repeated string group = 1;

  // Required. Names of member groups to be included in the groups.
  repeated string subgroup = 2;
}

// Describes the state the service instance groups should be in after the state is executed.
//
// If group configuration is valid in a specific system state, the configured state is applied to
// all group members. After that, the normal service instance configuration rules still apply:
// - "destroyed" > "started" > "created" precedence
// - not configured means the instance should be moved to the default state
//
// For the GroupsStates definition to be useful, at least one item should be present in any of the fields.
message GroupsStates {
  // Names of the groups that must be in a "created" state.
  repeated string created = 1;

  // Names of the groups that must be in a "started" state, overrides "created" state.
  repeated string started = 2;

  // Names of the groups that must not run, overrides all other states.
  repeated string destroyed = 3;
}

// Configuration for group states depending on the state of the system.
message GroupsStateConfiguration {
  // Condition for the system state under which the related group states should be executed by  Orchestrator.
  //
  // If omitted, the related groups states are always executed.
  Condition condition = 1;

  // Required. States of service bundle groups to be executed by Orchestrator if the condition  is true.
  GroupsStates groups_states = 2;
}

מצבי ההגדרה

מצבי ההגדרה (מצבים) מגדירים מתי מופעל, מופסק או נמחק מופע או קבוצה של שירות, והם מורכבים מ-conditions ו-instances_states (הגדרת חבילה) ו-groups_states (הגדרת מכונה וירטואלית).

תנאים

תנאים מאפשרים למודל להגדיר תנאי בוליאני, שאם הוא מקבל את הערך true, סטטוס המופע המוגדר מוחל. תנאי כולל את המאפיינים הבאים:

  • ביטוי בוליאני מורכב באופן שרירותי (שנוצר באמצעות ביטויי and או not) על סמך אותות נתמכים כמו עוצמה, רכב ומצב מותאם אישית.

  • (אופציונלי) מצב ההגדרה ללא תנאי תמיד פעיל, כלומר הערך שלו הוא true

מצבים של מופעים ומצבים של קבוצות

instances_states ו-groups_states מאופיינים בתכונות האלה.

  • להכתיב אילו מצבים נדרשים לסוכן התזמור כדי להחיל על המקרים או הקבוצות הנתונים, בהינתן שהמצב הוא active

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

המדינות שבהן השירות הזה נתמך:

  • אחרי שמתקשרים אל startedService::on_start.

  • created

    • אחרי שקוראים ל-Service::new, אבל לפני שקוראים ל-Service::on_start.

      או

    • אחרי שקוראים ל-Service::on_stop, אבל לפני שקוראים ל-Service::drop.

  • destroyed אחרי שמתקשרים אל Service::drop.

Ruleset

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

  • אם אף אחד מהמצבים הפעילים לא חל על מופע השירות, הוא מושמד.

  • כשמצב פעיל אחד או יותר חל, סדר העדיפות הזה חל:

    1. ל-destroyed יש עדיפות מוחלטת.
    2. started מקבל עדיפות לפני created.

סכמת Proto

הנה סכימת proto לדוגמה:

// A root boolean condition.
message Condition {
  // Required.
  oneof root {
    // VPM power state condition.
    string power_state = 1;
    // VPM vehicle state condition.
    string vehicle_state = 2;
    // Custom mode state condition.
    CustomState custom_state = 3;
    // Negation of a nested condition.
    Condition not = 4;
    // Logical 'and' between conditions grouped in expression.
    Expression and = 5;
    // Logical 'or' between conditions grouped in expression.
    Expression or = 6;
  }
}

// Representation of Custom state condition.
//
// Custom mode(s) are defined by the OEM and are not standardized by the platform, in contrast with
// VPM modes (i.e. power and vehicle mode).
message CustomState {
  // Custom mode being checked.
  string mode = 1;
  // State of the custom mode.
  string state = 2;
}

// A set of conditions united under an 'and' or 'or' expression.
//
// Evaluation type ('and' or 'or') depends on the field in [Condition]/[Expression], where the
// expression is being used.
//
// At least one value in at least one of the fields is required.
message Expression {
  // VPM power state condition.
  repeated string power_state = 1;
  // VPM vehicle state condition.
  repeated string vehicle_state = 2;
  // Custom mode state condition.
  repeated CustomState custom_state = 3;
  // Negation of a nested condition.
  repeated Condition not = 4;
  // Logical 'and' between conditions grouped in expression.
  repeated Expression and = 5;
  // Logical 'or' between conditions grouped in expression.
  repeated Expression or = 6;
}

אסטרטגיה לשחזור מקריסה ולהפעלה מחדש

ה-Orchestrator מספק מנגנון חזק לטיפול בקריסות של חבילות שירות ובכשלים במעבר בין שלבי מחזור החיים. ל-Orchestrator יש תצוגה הוליסטית של מצבי השירות, והוא מנהל את המעברים בין המצבים, ולכן הוא הרכיב המתאים ביותר להפעלת האסטרטגיה של הפעלה מחדש וניסיון חוזר. שירות הדיווח Lifecycle Manager ‏ (LM) מדווח על קריסות של חבילות שירות ל-Orchestrator באמצעות הודעות על סגירת binder. כדי להימנע מקריאות מיותרות של binder אל LM, ה-Orchestrator שומר במטמון את המצב האחרון של כל חבילה (בין אם היא הצליחה או לא) ולא מחיל מחדש מעברים אם המצב האחרון הידוע זהה למצב החדש המבוקש.

הגדרת ניסיון חוזר

אפשר להגדיר את אסטרטגיית ההפעלה מחדש והניסיון החוזר לכל מופע בהגדרות של כלי התזמור באמצעות retry_mapping. אם max_retries לא מוגדר בתצורה, ערך ברירת המחדל נלקח ממאפיין המערכת ro.boot.sdv.orchestrator.recovery.max_retries. אם לא מגדירים את המאפיין הזה, ערך ברירת המחדל הוא 0.

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

דוגמה להגדרה

service_bundle_config {
  package_name: "oem.package"
  service_bundle_name: "OemApplication"
  instance: "fog_front_light"
  instance: "fog_rear_light"

  # Defines the restart configuration mapping for specific instances.
  retry_mapping {
    instance: "fog_front_light"
    instance: "fog_rear_light"
    retry_config {
      max_retries: 3
    }
  }
}

התנהגות השחזור

הלוגיקה של ההפעלה מחדש והניסיון החוזר של Orchestrator מאפשרת לנהל בצורה חלקה כמה תרחישי כשל:

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

הכלי Orchestrator מבחין בין סוגים שונים של שגיאות שמוחזרות על ידי Lifecycle Manager כדי לקבוע את אסטרטגיית הניסיון החוזר:

  • שגיאות זמניות (SERVICE_NOT_FOUND, ‏ OPERATION_FAILED,‏ INTERNAL_ERROR): רכיב Orchestrator מנסה לבצע שוב את הפעולה בלי לבצע פעולות ניקוי מיוחדות.
  • שגיאות מתמשכות (VALUE_CORRUPTED, INVALID_ARGUMENT): המערכת Orchestrator מניחה שחבילת השירות נמצאת במצב פגום ומנסה להפסיק את מופע השירות לפני שמנסה שוב לבצע את הפעולה כדי להבטיח הפעלה מחדש תקינה.
  • שגיאות קבועות (PERMISSION_DENIED): המערכת לא מנסה לבצע את הפעולה שוב, והחבילה נחשבת למצב שלא ניתן לשחזר.

אם Lifecycle Manager קורס, כל התהליכים של חבילת השירותים אובדים. מכיוון שהמצב בפועל לא ידוע, המערכת לניהול תזמור (Orchestrator) מבטלת את התוקף של כל מופע ומפעילה אסטרטגיה להפעלה מחדש עם ניסיונות חוזרים שנותרו, כדי להביא כל מופע למצב האחרון שנדרש.

כדי למנוע לולאות שחזור אינסופיות לחבילות שקורסות או נכשלות שוב ושוב, מונה הניסיונות חוזר ל-max_retries רק אחרי שמסתיימת בהצלחה פעולה במחזור החיים, או כשמתקבלת בקשה למעבר למצב חדש. אם חבילה ממצה את מכסת הניסיונות החוזרים שלה בגלל כשלים רצופים (לדוגמה, כשל במעבר ואחריו קריסה), היא לא מופעלת מחדש עד שמונה הניסיונות החוזרים מאופס.

דיווח על מצב המכשיר לכלי תקינות המערכת

ה-Orchestrator חושף ממשק פנימי של binder שה-Health Monitor‏ (HM) נרשם אליו, וכך הוא יכול לקבל עדכונים שוטפים לגבי המצב של כל חבילות השירות. באמצעות הממשק הזה, כלי התזמור מדווח באופן פעיל על:

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

המידע הזה מדווח על כל המקרים, כולל מקרים שלא נרשמו למעקב אחר אותות חיים. ‫HM משתמש במידע הזה כדי להטמיע את ממשקי ה-API שלו ולדווח על מצב המכונה הווירטואלית. מידע נוסף זמין במאמר בנושא מעקב אחר נתוני בריאות.

דוגמאות

בקטע הזה מוצגות דוגמאות להגדרת מצבים עם תנאים.

דוגמה של שירות בסיסי
  • אין לו תנאי ולכן הוא תמיד פעיל.
  • מפעיל מופע שירות יחיד.
state {
  # Note: This state has no condition, hence its actions are valid throughout the lifetime of the program
  instances_states { started: "ServiceBundleName" }
}
דוגמה לאפליקציה של בקרת אקלים
  • תנאי: פעיל כש-custom_state != occupancy.OCCUPANCY_EMPTY || custom_state == preheat.PREHEAT_ON.

  • הצהרה על כמה מופעים של שירות שקשור ל-HVAC כstarted.

state {
  condition {
    or {
      # I.e. when the vehicle is occupied (for example, by _DRIVER / _NON_DRIVER / _PET)
      not {
        custom_state {
          mode: "occupancy"
          state: "OCCUPANCY_EMPTY"
        }
      }
      custom_state {
        mode: "preheat"
        state: "PREHEAT_ON"
      }
    }
  }

  # HVAC-related services
  instances_states {
    started: "HvacTemperatureCommand"
    started: "TempSensorDriverZone"
    started: "TempSensorPassengerZone"
    started: "RefrigerantLoop"
  }
}
דוגמה לחיסכון באנרגיה
  • תנאי: פעיל כש-custom_state == system_power.SYSTEM_POWER_LOW && custom_state == range_ext.RANGE_EXT_ON.

    המצב הזה יכול להיחשב כמצב של חיסכון באנרגיה.

  • הצהרה על מופע שירות אחד שקשור ל-HVAC כ-destroyed.

  • בדוגמה הזו, כשמצבי SYSTEM_POWER_LOW ו-RANGE_EXT_ON פעילים, אפליקציית בקרת האקלים פועלת בלי מופע השירות RefrigerantLoop:

    state {
      condition {
        and {
          custom_state {
            mode: "system_power"
            state: "SYSTEM_POWER_LOW"
          }
          custom_state {
            mode: "range_ext"
            state: "RANGE_EXT_ON"
          }
        }
      }
    
      # Disable services with high power consumption
      instances_states { destroyed: "RefrigerantLoop" }
    }
    
דוגמה לשימוש ב-Life onboard
  • תנאי: פעיל אם power_state == ON && vehicle_state == LIFE_ON_BOARD.

    אפשר לראות את המצב הזה כמישהו נמצא ברכב והרכב מופעל.

  • מצהיר על חיישני טמפרטורה כפועלים.

  • כשמישהו נמצא ברכב, מתבצע מעקב אחרי פריטים כמו טמפרטורה מסיבות בטיחותיות.

state {
  condition {
    and {
      power_state: "ON"
      vehicle_state: "LIFE_ON_BOARD"
    }
  }

  # Temperature monitoring services
  instances_states {
    started: "TempSensorDriverZone"
    started: "TempSensorPassengerZone"
  }
}

דוגמאות

בקטע הזה מוצגות דוגמאות מלאות שכוללות:

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

  • הגדרת פרוטו ברמת המכונה הווירטואלית שמציגה לוגיקה לאינטראקציה עם הקבוצות על סמך מצבים

הגדרה ברמת חבילת השירות

# proto-file: //system/software_defined_vehicle/orchestration/distributed_config/src/protos/service_bundle_config.proto
# proto-message: ServiceBundleConfig

package_name: "oem.package"
service_bundle_name: "OemApplication"
instance: "fog_front_light"
instance: "fog_rear_light"
instance: "turn_signal_light"
custom_mode: "FOG"
custom_mode: "TURN"

group_mapping {
    group: "fog_light"
    instance: "fog_front_light"
    instance: "fog_rear_light"
}

group_mapping {
    group: "flasher_light"
    instance: "turn_signal_light"
}

state {
    condition {
        power_state: "ON"
    }

    instances_states {
        created: "turn_signal_light"
        destroyed: "fog_front_light"
        destroyed: "fog_rear_light"
    }
}

הגדרה ברמת ה-VM

# proto-file: //system/software_defined_vehicle/orchestration/distributed_config/src/protos/vm_config.proto
# proto-message: VmConfig

group_mapping {
    group: "lights"
    subgroup: "fog_light"
    subgroup: "flasher_light"
}

state {
    condition {
        custom_state {
            mode: "FOG"
            state: "ON"
        }
    }
    groups_states {
        started: "fog_light"
    }
}

state {
    condition {
        custom_state {
            mode: "TURN"
            state: "RIGHT"
        }
    }
    groups_states {
        started: "flasher_light"
    }
}

state {
    condition {
        vehicle_state: "SUSPEND_TO_RAM_ENTER"
    }
    groups_states {
        created: "lights"
    }
}

הגדרת מקביליות של ניהול חבילות

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

  1. ‫Orchestration Engine: השירות הזה קורא את המאפיין כדי לקבוע כמה שיחות בו-זמניות (לדוגמה, startService, stopService) יכול ה-Orchestrator לבצע אל Lifecycle Manager. זה חשוב מאוד לביצועים במהלך האתחול ומעברי מצב, שבהם הרבה חבילות עשויות לשנות את המצב שלהן בו-זמנית.

  2. ‫Lifecycle Manager: השירות הזה משתמש בערך המאפיין כדי לחשב את הגודל של מאגר השרשורים של Binder, שאחראי לטפל בכל הבקשות הנכנסות. כך מובטח למודל השפה שיש מספיק שרשורים כדי לטפל בבקשות המקבילות מהכלי לניהול תזמור.

אם הנכס הזה לא מוגדר, ערך ברירת המחדל של שני השירותים הוא 12.

שיטת ההגדרה

אפשר להגדיר את המאפיין בקובץ BoardConfig.mk של המכשיר על ידי הוספתו למשתנה BOARD_BOOTCONFIG. כך מוודאים שהערך יחול בכל פעם שהמכשיר מופעל.

BOARD_BOOTCONFIG += \
    androidboot.sdv.max_bundles_management_threads=8

כדי לשנות את הערך של המכשיר, צריך לשנות את השורה הזו בקובץ BoardConfig.mk המתאים ולבנות מחדש.

אופטימיזציה של זמן האתחול

‫ro.sdv.orchestrator.state.ready הוא מאפיין בוליאני write-once שמהווה חלק משיטת אופטימיזציה של הביצועים בזמן האתחול. ההודעה מציינת שהסוכן של Orchestration סיים את האתחול שלו ומוכן להתחיל לנהל את מחזור החיים של חבילות השירות. המטרה העיקרית שלו היא לתת עדיפות להפעלה של Orchestrator וחבילות השירות המנוהלות שלו, על ידי שליטה ברצף ההפעלה של סוכני SDV אחרים.

  • ההגדרה נקבעה על ידי: סוכן התזמור.
  • מתי: פעם אחת במהלך רצף האתחול.
  • שימוש: המערכת משתמשת במאפיין הזה כדי לשלוט ברצף ההפעלה של רוב הסוכנים של SDV (Updates Manager, ‏ VSIDL provider, ‏ Health Monitor, ‏ Service Discovery, ‏ Data Tunnel, ‏ RPC, ‏ VPM ו-Telemetry). אם מתחילים את כלי התזמור מוקדם ומגדירים סוכנים אחרים להמתין למאפיין הזה, המערכת מוודאת שכלי התזמור יוכל להתחיל את המשימה הקריטית שלו של הפעלת חבילות שירותים בלי להתחרות על משאבי המערכת.

ביצועים

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

  1. מאגר חבילות השירות: מתחיל ראשון כדי לטעון את כל המטא-נתונים של חבילות השירות.
  2. ‫Lifecycle Manager ו-Orchestrator: סוכני הליבה האלה מתחילים לפעול ברגע שהרישום מוכן. התחלה מוקדמת היא קריטית, כי היא מאפשרת ל-Orchestrator להתחיל להעריך את ההגדרה שלו ולהתכונן להפעלת חבילות שירות באופן מיידי.
  3. סוכני SDV אחרים: יתחילו לפעול רק אחרי שהכלי לתזמור מוכן.

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

מצבים שנצרכים על ידי Orchestrator

סוכן התיאום שומר על מינוי פעיל למצבי הרכב וההספק שמועברים על ידי VPM. במהלך יצירת החיבור הראשוני בין Orchestrator לבין מערכת ניהול הרכב וההספק (VPM),‏ Orchestrator מגדיר את מאפייני המערכת הבוליאניים הבאים לערך true:

  • sdv.orchestrator.bootup.power_mode.ready

  • sdv.orchestrator.bootup.vehicle_mode.ready

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

מצבי הרכב והטעינה

סוכן ניהול ההספק ומצב הרכב (VPM) מאפשר לרכיבי SDV לקבל מידע על המצב הנוכחי של הרכב, כמו מצב הפעולה (לדוגמה, חנייה או נסיעה) וסטטוס ההפעלה (לדוגמה, מופעל או מושהה). הכלי Orchestrator מעריך את הערכים האלה כדי להגדיר אילו חבילות שירותים יפעלו על סמך ההגדרה של הכלי. מידע נוסף מופיע במאמר בנושא ניהול כלי רכב וצריכת חשמל.

מצבים מותאמים אישית

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

פונקציונליות:

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

  • הרכב: כל שינוי במצב מותאם אישית מורכב משני רכיבים:

    • שם: מזהה ייחודי שנבחר על ידי יצרן הציוד המקורי (OEM) כדי לייצג את המצב המותאם אישית.

    • ערך: המצב הנוכחי של מצב מותאם אישית, שיכול להיות UNDEFINED אם לא מוגדר ערך.

  • תפקיד המתאם: המתאם פועל כמקבל פסיבי של ערכים במצב מותאם אישית.

  • תפקיד חבילת שירותים: כל חבילת שירותים יכולה להיות הבעלים של כמה מצבים מותאמים אישית, ויכולה לפרסם ערכים חדשים בכל אחד מהם. יכול להיות שלכמה חבילות שירותים יהיה אותו מצב מותאם אישית, כלומר מצב מותאם אישית יכול לקבל ערכים חדשים ממקורות שונים.

  • אחריות לאימות: יצרני ציוד מקורי (OEM) אחראים לוודא שמעברי המצבים תקינים. הכלי Orchestrator מקבל כל ערך חדש.

מאפיינים משוערים:

  • מספר משוער: מצבי הפעלה ומצבי רכב מנהלים את מחזור החיים של רוב חבילות השירות, ומצבים מותאמים אישית ממלאים תפקיד משלים. אנחנו צופים שסדר הגודל של המצבים המותאמים אישית יהיה עשרות ולא מאות.

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

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

מצבים נתמכים

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

כדי להצהיר על המצבים המותאמים אישית שחבילת שירותים יכולה לפרסם (ולכן היא הבעלים שלהם), סכמת הפרוטו service_bundle_config מורחבת עם הרכיבים הבאים:

// Service bundle configuration.
//
// Defines service bundle metadata, its instances, and configuration for instances
// lifecycle states depending on the state of the vehicle.
message ServiceBundleConfig {
      [...]

    // The list of custom modes that this service bundle publishes.
    repeated string custom_mode = 5;
}

הגדרת חבילות על סמך מצבים

הגדרת ה-proto הקיימת של Orchestrator (רכיב Condition) תומכת בשינוי של חבילות שירותים על סמך מצבים מותאמים אישית:

message Condition {
  oneof root {
    string power_state = 1;
    string vehicle_state = 2;
    CustomState custom_state = 3;
    Condition not = 4;
    Expression and = 5;
    Expression or = 6;
  }
}

message CustomState {
  // Custom mode being checked.
  string mode = 1;
  // State of the custom mode.
  string state = 2;
}

דוגמה של proto

בדוגמה הבאה אפשר לראות איך מגדירים הפעלה של מופעים של חבילת שירות על סמך מצב TURN וFOG:

service_bundle_config {
package_name: "oem.package"
service_bundle_name: "OemApplication"
instance: "flasher_light"
// Service bundle is allowed to set values for the TURN mode
custom_mode: "TURN"
// Service bundle is allowed to set values for the FOG mode
custom_mode: "FOG"

state {
    condition {
        custom_state {
            mode: "TURN"
            state: "LEFT"
        }
    }
    instances_states {
        started: "flasher_light"
    }
}

state {
    condition {
        custom_state {
            mode: "FOG"
            state: "ON"
        }
    }
    // Group assumed to be defined containing all FOG lights instances.
    groups_states {
        started: "fog_lights"
    }
}

}

הגדרת מצבים חדשים בהתאמה אישית

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

RPC

כל Orchestrator שפועל בכל מכונה וירטואלית יוצר שרת RPC כדי להאזין לערכים חדשים של מצב מותאם אישית. כל חבילת שירותים שרוצה לעדכן מצב מותאם אישית צריכה ליצור לקוח RPC לשרת. רשימות ACL נאכפות כדי למנוע מצרורות לא מורשים להתחבר לשרת.

הגדרת הפרוטו להגדרת ערך חדש באמצעות RPC נראית כך: example:

syntax = "proto3";

import "google/protobuf/timestamp.proto";

package com.sdv.google.Orchestrator;

// Representation of the request used by service bundles to update a custom mode.
// Service bundles are permitted to update only the custom modes specifically designated
// for them within the Orchestrator configuration.
message SetCustomStateRequest {
  // Required.
  // The name of the custom mode.
  // The mode string can not be longer than 56 characters and can only contain
  // letters, numbers, dashes, dots and underscores: [a-zA-Z0-9_-.].
  // No other special character nor spaces should be present in the mode.
  string mode = 1;

  // Required.
  // The new value for the custom mode.
  // The value string can not be longer than 56 characters and can only contain
  // letters, numbers, dashes, dots and underscores: [a-zA-Z0-9_-.].
  // No other special character nor spaces should be present in the value.
  string value = 2;

  // Required.
  // The timestamp in which the new custom mode value was set. This is used to
  // prevent race conditions whenever different service bundles in different
  // VMs want to set a new value for the same custom mode.
  // We use this timestamp to order the requests and we promise eventual
  // consistency: while temporary inconsistencies may occur, the system will
  // eventually converges to the correct state.
  .google.protobuf.Timestamp timestamp = 3;
}

// Representation of the set custom state response.
message SetCustomStateResponse {}

// Orchestrator interface for service bundles that update the value of a
// custom mode.
// When a new value is received, it is propagated to Orchestrators running on
// other VMs.
service CustomStateService {
  // Updates the value for the custom mode.
  // Returns the error:
  // - PermissionDenied: the service is not authorized to update the custom mode.
  // - InvalidArgument: the provided mode and/or value are not valid.
  rpc SetCustomState(SetCustomStateRequest) returns (SetCustomStateResponse) {};
}

ביטול של מעברים בין מצבי צריכת חשמל

ה-Orchestrator מאפשר לבטל מעברים מתמשכים בין מצבי צריכת חשמל באמצעות SHUTDOWN_CANCELLED מצב צריכת החשמל (שנשלח על ידי VPM ל-Orchestrator).

לדוגמה, נניח שיש לכם את ההגדרה הבאה ב-Orchestrator:

state {
    condition {
        power_state: "SUSPEND_TO_RAM_ENTER"
    }
    instances_states {
        started: "instance-1"
        started: "instance-2"
        started: "instance-3"
    }
}

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

תרחיש 1: המצב הנוכחי הוא מצב צריכת חשמל

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

דוגמה: אם instance-1 מההגדרה בדוגמה הקודמת נמצא בתהליך של הפעלה כשמתקבל מצב SHUTDOWN_CANCELLED, הפעלת instance-1 תושלם. עם זאת, instance-2 ו-instance-3 לא עוברים למצב התחלתי.

תרחיש 2: מצב צריכת חשמל קיים בתור לעיבוד

אם ה-Orchestrator מעבד עדכון של מצב שאינו מצב צריכת חשמל, ויש בקשת הפעלה בתור של המצבים לביצוע, המעבר למצב צריכת חשמל מוסר מהתור. כך נמנעת ההרצה שלו.

דוגמה: אם משתמשים בהגדרה שבדוגמה הקודמת, והכלי Orchestrator פועל על עדכון שלא קשור להפעלה (למשל עדכון של רכב) וההודעה SUSPEND_TO_RAM_ENTER נמצאת בתור, קבלת ההודעה SHUTDOWN_CANCELLED לא תגרום להפעלה של אף אחד מהמופעים (instance-1, ‏ instance-2, ‏ instance-3).

דוגמה להטמעה

הקטלוג של לקוח שרוצה להשתמש בקוד שנוצר על ידי תוכנת ביניים כדי ליצור לקוח לשרת RPC יכול להיראות כמו בדוגמה הבאה:

# proto-file: //system/software_defined_vehicle/vsidl/language/src/protos/sdv/vsidl/v1/syntax.proto
# proto-message: VsidlEntry

package: "package_name"

service_bundle {
    name: "service_bundle_name"

    client {
        service: "com.android.sdv.orchestrator.CustomStateService"
    }
}

כשיוצרים קוד, צריך להוסיף את התלות לקטלוג של Orchestrator:

--dependency-catalog-path orchestration/engine/stable/vsidl/*

קוד הלקוח לשליחת ערך חדש נראה כך:

let fqin = ServiceFqin::builder()
        .sdv_vm_name("vm_name")
        .sdv_package_name("package_name")
        .service_bundle_name("service_bundle_name")
        .service_instance_name("instance_name")
        .build()
        .unwrap();
let context_ref = ContextRef::create(fqin);
let comms = Arc::new(SdvComms { context: context_ref });
// service_bundle_name is the bundle generated with middleware code that defines
// the RPC client to "com.android.sdv.orchestrator.CustomStateService".
let client = service_bundle_name::new(comms).await.unwrap();
let rpc_client = client
        .create_rpc_client::<Client>(
            UnitName::builder()
                .vm_name(comms.context.get_self_fqin().get_sdv_vm_name())
                .package_name("com.android.sdv.orchestrator")
                .bundle_name("OrchestratorServiceBundle")
                .service_unit_name(Client::DEFAULT_UNIT_NAME)
                .build()
                .unwrap(),
            ClientOptions::default(),
        )
        .await;
let client = Arc::new(rpc_client.unwrap());
let request = SetCustomStateRequest {
mode: custom_mode_name,
value: custom_mode_value,
timestamp: MessageField::some(Timestamp::now()),
..Default::default()
};
let result = client.SetCustomState(&request).await;
// Process result

כלים לניפוי באגים

הסוכן של Orchestrator תומך בכלי dumpsys. כדי להפעיל אותו, מריצים את הפקודה הבאה במופע SDV פעיל:

adb shell dumpsys com.google.sdv.ISdvAgent/orch

אפשר להשתמש בכלי הזה כדי לבצע ניפוי באגים ולקבל תובנות לגבי המצב הפנימי של סוכן Orchestrator. כשתעשו את זה, יוצגו לכם:

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

לדוגמה:

AGENT NAME: SDV Agent dump - Orchestrator
AGENT FQIN: instance1:com.android.sdv.orchestrator.OrchestratorServiceBundle/default
AGENT STATE: See orchestrator state below.
----------------
----------------
INTERNAL STATE REPORTERS:

*NAME: Configuration state
*REPORT:
Active modes:
MODE                          VALUE                         TIMESTAMP (scs, ns)
Power                         POWER_OFF_EXIT                -
Vehicle                       VEHICLE_ON                    -
Custom("CHARGING")            ON                            1750757590 (scs) 466507459 (ns)
Custom("TIRE_PRESSURE")       front-left                    1750757570 (scs) 554522995 (ns)

Modes allowed to publish by bundle (FQIN: modes):
com.android.sdv.sample.orchestration/CustomModeControlBundle: CHARGING, TIRE_PRESSURE

Requested state for instances:
STATE          FQIN
Started        com.android.sdv.sample.orchestration/CustomModeControlBundle/always-started-instance
Started        com.android.sdv.sample.orchestration/OrchestratedServiceBundle/my-instance
Started        com.sdv.oem.sample.diagnostics/DiagnosticsSampleWithDataItem/instance
Started        com.sdv.oem.sample.diagnostics/DiagnosticsSampleWithEvent/instance
Started        com.sdv.oem.user_preferences/UserPreferencesServiceBundle/default
----------------
*NAME: Engine state
*REPORT:
Last mode enforced was Custom("CHARGING") with value "ON"

Next modes to process: []
----------------

כדי לקבל מידע נוסף על חבילות שירותים נפרדות שמנוהלות על ידי Orchestrator, אפשר להשתמש ב-dumpsys הקיים מ-Lifecycle Manager באופן הבא:

dumpsys google.sdv.lifecycle.ILifecycleManager/default

כך אפשר לקבל מידע מפורט על מצב מחזור החיים של כל שירות. שילוב של פלט dumpsys של Orchestrator עם פלט של LifecycleManager מאפשר לקבל תמונה מלאה של מחזור החיים של חבילות השירות במכונה הווירטואלית.