ארכיטקטורה של העדפות משתמש ומדריך הטמעה

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

סקירה כללית של הארכיטקטורה

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

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

הטמעה של שירות שניתן לשליטה על ידי המשתמש

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

כדי לאפשר לשירות שלכם להיות נשלט על ידי העדפות המשתמש (ומשתמש עם HMI), פועלים לפי השלבים שבקטע הזה.

  1. מגדירים את ממשקי השירות בקובץ VSIDL:

    • כשרת, צריך להטמיע את com.sdv.google.user_preferences.user_controllable.UserControllableService. כך הסוכן יוכל לשלוח לכם בקשות לשינויים.
    • הלקוחות יצטרכו להשתמש ב-com.sdv.google.user_preferences.UserPreferencesRegistryService. הפעולה הזו תרשום את ההגדרות שלכם בהפעלה.

    בדוגמה הבאה מוצג service_bundle.vsidl לשירות שאפשר לשלוט בו באמצעות משתמש:

    service_bundle {
        name: "MyFeatureService"
        server {
            service: "com.sdv.google.user_preferences.user_controllable.UserControllableService"
        }
        client {
            service: "com.sdv.google.user_preferences.UserPreferencesRegistryService"
        }
    }
    
  2. הגדרות הרישום מופעלות באתחול בפרוטוקול המפתח user_preferences_registry_service.proto:

    1. התחברות אל UserPreferencesRegistryService.
    2. יוצרים מופע של RegisterSettingsRequest.
    3. מגדירים מופע של SettingsGroup (אוסף לוגי של הגדרות).
    4. לכל הגדרה, מגדירים:

      *   **Key:** Unique string ID (for example, `TEMPERATURE`)
      *   **Kind:** `PER_USER` (stored per user profile) or `SHARED`
          (global)
      *   **Default value:** Initial value if no user preference exists
      *   **Constraints:** (optional) Validation rules (for example, Min
          16, Max 32 for HVAC).
      
    5. רוצה להתקשר ל-RegisterSettings()?

    הדוגמה הבאה היא ב-Rust קונספטואלית:

    let temperature_setting = SettingDefinition {
        name: "TEMPERATURE".to_string(),
        kind: SettingKind::PER_USER.into(),
        default_value: Value::Int64(22), // Default 22 degrees
        constraint: Some(Constraints::Int64Constraints(Int64Constraints {
            min_value: Some(16),
            max_value: Some(32),
            ..Default::default()
        })),
        ..Default::default()
    };
    
    registry_client.RegisterSettings(&RegisterSettingsRequest {
        group_name: "HVAC".to_string(),
        version: "1.0".to_string(),
        settings_definitions: vec![temperature_setting],
    }).await?;
    
  3. איך מטפלים בבקשות לשינוי הגדרות ב-user_controllable_service.proto:

    1. מטמיעים את ה-RPC‏ RequestSettingsChange.
    2. בודקים אם הערכים המבוקשים תקפים בהקשר הנוכחי (לדוגמה, אם החומרה מוכנה).
    3. להחיל לוגיקה ספציפית כדי לבצע את השינוי (לדוגמה, להזיז את המושב, לשנות את מהירות המאוורר).
    4. החזרת הערכים שהוחלו ב-RequestSettingsChangeResponse:
    5. אם אישרתם את השינוי, מחזירים את הערך החדש.
    6. אם דחיתם או הגבלתם את הערך, מחזירים את הערך שהגדרתם (או השארתם).

    הדוגמה הבאה היא ב-Rust קונספטואלית:

    async fn RequestSettingsChange(
        &self,
        _caller_id: ServiceFqin,
        request: &RequestSettingsChangeRequest
    ) -> SdvResult<RequestSettingsChangeResponse> {
        let mut applied_settings = Vec::new();
    
        for setting in &request.settings {
            if self.hardware.set_value(setting.key, setting.value).is_ok() {
                // Change accepted
                applied_settings.push(setting.clone());
            } else {
                // Change rejected, return current actual value
                let current_val = self.hardware.get_value(setting.key);
                applied_settings.push(create_setting(setting.key, current_val));
            }
        }
    
        Ok(RequestSettingsChangeResponse {
            settings: applied_settings,
        })
    }
    
  4. מטמיעים את FactoryReset RPC כדי להחזיר את מערכת המשנה למצב נקי:

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

תאימות לאחור והתפתחות של סכימות

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

שינויים תואמים

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

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

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

שינויים לא תואמים

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

  • מסירים הגדרה קיימת.

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

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

טיפול בשינויים שעלולים לשבור את התאימות לאחור

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

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

ההטמעה של RequestSettingsChange השירות צריכה להבטיח ששינוי בהגדרה אחת ישתקף בהגדרה המקבילה שהוצאה משימוש, ושינוי בהגדרה המקבילה ישתקף בהגדרה.

לוגיקה לדוגמה של תאימות:

  1. שירות מוציא משימוש את ההגדרה הישנה TEMPERATURE_C (צלזיוס) ומציג את ההגדרה TEMPERATURE_K (קלווין).

  2. כשהסוכן שולח בקשה לעדכון ההגדרה חדש:

    • השירות מקבל בקשה ל-TEMPERATURE_K (לדוגמה, 295.15 K).

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

  3. כשהסוכן שולח בקשה להגדרה שהוצאה משימוש מלקוח מדור קודם:

    • השירות מקבל בקשה ל-TEMPERATURE_C (לדוגמה, 24°C).
    • השירות ממיר את הערך הזה לערך בקלווין (297.15 K) ומעדכן ושומר את שני הערכים באופן פנימי.

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

הטמעה של לקוח

כדי להטמיע לקוח (לדוגמה, HMI) שיוצר אינטראקציה עם נציג העדפות המשתמש:

  1. מגדירים את חבילת שירות הלקוח כדי ליצור אינטראקציה עם ממשקי העדפות משתמש ספציפיים. בקובץ ה-VSIDL:

    • כשרת, צריך להטמיע את com.sdv.google.user_preferences.view.ChangeNotifier כדי לאפשר לסוכן לשלוח ללקוח התראות בזמן אמת על שינויים בהגדרות.
    • כלקוח, אפשר להשתמש ב-com.sdv.google.user_preferences.UserPreferencesManagementService כדי לבקש שינויים בהגדרות ולהירשם לעדכונים.
    • כלקוח, משתמשים ב-com.sdv.google.user_preferences.UserPreferencesAdminService כדי לנהל פרופילי משתמשים (לדוגמה, ליצור, לבחור, למחוק ולאפס להגדרות המקוריות).

    הדוגמה הבאה מציגה קובץ service_bundle.vsidl של לקוח:

    service_bundle {
        name: "MyHmiClient"
        server {
            service: "com.sdv.google.user_preferences.view.ChangeNotifier"
        }
        client {
            service: "com.sdv.google.user_preferences.UserPreferencesManagementService"
        }
        client {
            service: "com.sdv.google.user_preferences.UserPreferencesAdminService"
        }
    }
    

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

    server {
        service: "com.sdv.google.user_preferences.view.ChangeNotifier"
        allow_all_channels: true
    }
    client {
        service: "com.sdv.google.user_preferences.UserPreferencesManagementService"
        allow_all_channels: true
    }
    client {
        service: "com.sdv.google.user_preferences.UserPreferencesAdminService"
        allow_all_channels: true
    }
    
  2. אפשר לשנות את הגדרות הבקשה בUserPreferencesManagementService:

    1. התחברות אל UserPreferencesManagementService.
    2. יוצרים מופע של RequestSettingsChangeRequest ומציינים את SettingsGroupId ואת ההגדרות הרצויות.
    3. אופציונלי. מגדירים את ChangePersistencePolicy ל-PERSISTENT_CHANGE (ברירת מחדל) או ל-NON_PERSISTENT_CHANGE.
    4. רוצה להתקשר ל-RequestSettingsChange()?

    הדוגמה הבאה היא ב-Rust קונספטואלית:

    management_client.RequestSettingsChange(&RequestSettingsChangeRequest {
        settings_group_id: Some(SettingsGroupId {
            service_fqin: hvac_service_fqin.to_string(),
            name: "HVAC".to_string(),
            ..Default::default()
        }).into(),
        settings: vec![Setting {
            key: "TEMPERATURE".to_string(),
            value: Some(Value::Int64(24)),
            ..Default::default()
        }],
        change_persistence_policy: ChangePersistencePolicy::PERSISTENT_CHANGE.into(),
        ..Default::default()
    }).await?;
    
  3. הרשמה לקבלת עדכונים על שינויים בהגדרות באמצעות user_preferences_management_service.proto ו-change_notifier.proto:

    1. מטמיעים את ה-RPC‏ OnSettingsChange מהממשק ChangeNotifier בחבילת השירות. סוכן העדפות המשתמש מפעיל את השיטה הזו כשמתרחש שינוי.
    2. התחברות אל UserPreferencesManagementService.
    3. יוצרים SubscribeToSettingsChangeAndGetSettingsRequest ומציינים את SettingsGroupId שרוצים לעקוב אחריו.
    4. כדי להחזיר את מצב ההגדרות ואז לשלוח עדכונים עתידיים דרך ההטמעה של OnSettingsChange, צריך לבצע קריאה אל SubscribeToSettingsChangeAndGetSettings().

    הדוגמה הבאה להטמעה של הממשק ChangeNotifier היא ב-Rust קונספטואלית:

    #[async_trait]
    impl ChangeNotifier for MyHmiServiceImpl {
        async fn OnSettingsChange(
            &self,
            _caller_id: ServiceFqin,
            request: &OnSettingsChangeRequest,
        ) -> SdvResult<OnSettingsChangeResponse> {
            // Process the active_settings, pending_changes, and persisted_settings
            // Update your UI or internal state accordingly.
            info!("Received settings change for group: {}", request.settings_group_id.name);
            // ...
            Ok(OnSettingsChangeResponse::new())
        }
    }
    

    הדוגמה הבאה להרשמה היא ב-Rust קונספטואלית:

    management_client.SubscribeToSettingsChangeAndGetSettings(&SubscribeToSettingsChangeAndGetSettingsRequest {
        settings_group_id: Some(SettingsGroupId {
            service_fqin: hvac_service_fqin.to_string(),
            name: "HVAC".to_string(),
            ..Default::default()
        }).into(),
        ..Default::default()
    }).await?;
    
  4. אופציונלי: לקוחות שמנהלים פרופילים של משתמשים (לדוגמה, אפליקציית הגדרות ייעודית) יכולים להשתמש ב-user_preferences_admin_service.proto כדי ליצור אינטראקציה עם שירות האדמין:

    1. התחברות אל UserPreferencesAdminService.
    2. אפשר להשתמש ב-RPC כמו CreateUser,‏ SelectUser,‏ DeleteUser,‏ FactoryReset ו-ListUsers כדי לנהל פרופילי משתמשים.

    הדוגמה הבאה ליצירת משתמש היא ב-Rust קונספטואלי:

    admin_service_client.CreateUser(&CreateUserRequest {
        user: Some(User {
            id: 1,
            flags: UserFlags::DRIVER.value(),
            ..Default::default()
        }).into(),
        ..Default::default()
    }).await?;
    
  5. אפשר לגלות את ההגדרות והמשתמשים הזמינים באמצעות שירות הניהול עם user_preferences_admin_service.proto:

    1. כדי לקבל רשימה של כל המשתמשים הרשומים, קוראים ל-ListUsers() ב-UserPreferencesAdminService.
    2. כדי לקבל את ההגדרות של משתמש ספציפי, קוראים ל-GetUserSettings(user_id) ב-UserPreferencesAdminService.

    הדוגמה הבאה של רישום משתמשים וקבלת הגדרות היא ב-Rust קונספטואלי:

    // List all users
    let list_users_response = admin_service_client.ListUsers(&ListUsersRequest::new()).await?;
    info!("Available users: {:?}", list_users_response.users);
    
    // Get settings for a specific user (for example, user with ID 1)
    if let Some(user_id) = list_users_response.users.first().map(|u| u.id) {
        let get_settings_response = admin_service_client.GetUserSettings(&GetUserSettingsRequest {
            user_id,
            ..Default::default()
        }).await?;
        info!("Settings for user {}: {:?}", user_id, get_settings_response.groups);
    }
    

סיכום התהליך

  1. הלקוח מתחיל את השימוש בשירותי הניהול והאדמיניסטרציה של הסוכן ומתחבר אליהם.
  2. הלקוח מאתר קבוצות של הגדרות ונרשם אליהן, ומציג את המצב הראשוני.
  3. הלקוח שולח RequestSettingsChange לסוכן.
  4. הנציג שולח ללקוח התראה של OnSettingsChange עם הגדרות ומצב מעודכנים.

דוגמה מלאה שעובדת מופיעה בקובץ HMIService בתיקייה @samples/user_preferences/v1/.

הטמעה של הסוכן

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

בנוסף, מסופק קובץ user_preferences_sample.vsidl של חבילת שירות לדוגמה לסוכן:

package: "com.sdv.oem.user_preferences"

service_bundle {
    name: "UserPreferencesServiceBundle"
    server {
        service: "com.sdv.google.user_preferences.UserPreferencesAdminService"
    }
    server {
        service: "com.sdv.google.user_preferences.UserPreferencesManagementService"
    }
    server {
        service: "com.sdv.google.user_preferences.UserPreferencesRegistryService"
    }
    client {
        service: "com.sdv.google.user_preferences.view.ChangeNotifier"
    }
    client {
        service: "com.sdv.google.user_preferences.user_controllable.UserControllableService"
    }
}

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

sdv_service_bundle_metadata {
  # This name must match the agent's Bundle Name
  name: "UserPreferencesServiceBundle"
  version_number: 1
  version_name: "1"

  native_library_path: "lib64/<USER-PREFERENCES-FFI-LIB>.so"

  orchestration_config_path: "etc/user_preferences_service_bundle/<USER-PREFERENCES-ORCHESTRATION>.textproto"

  authorization_policy_path: "etc/user_preferences_service_bundle/permissions.textproto"
}

בקובץ מדיניות ההרשאות .textproto של הלקוח נדרשות ההרשאות הבאות:

client {
    service: "com.sdv.google.user_preferences.UserPreferencesManagementService"
    allow_all_channels: true
}

client {
    service: "com.sdv.google.user_preferences.UserPreferencesRegistryService"
    allow_all_channels: true
}

client {
    service: "com.sdv.google.user_preferences.UserPreferencesAdminService"
    allow_all_channels: true
}

נספח: מושגי מפתח

בקטע הזה מתוארים כמה מושגים חשובים.

משתמשים

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

message User {
  // Required.
  // A unique ID for the user.
  int32 id = 1;

  // Bit flags that define properties of user. Integer values have to be powers of 2 as they are used as
  // a bit mask.
  enum UserFlags {
    // Due to Protobuff requirement to have the first enum option set to 0,
    // Assign 0 to an unused flag
    UNSET = 0x0;
    // Marks the user as vehicle driver
    DRIVER = 0x01;
    // Ephemeral users have non-persistent state, once another user is selected
    // the profile is deleted automatically
    EPHEMERAL = 0x02;
  }

  // Required.
  // Bitmask for the user flags defined above
  int32 flags = 2;
}

קבוצות של הגדרות

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

message SettingsGroup {
  // Required.
  // The identifier for this group.
  SettingsGroupId id = 1;

  // Required.
  // The version number of schema used by this setting group.
  string version = 2;

  // Required.
  // The list of settings within the setting group.
  repeated Setting settings = 3;
}

הגדרות

ההגדרה, message (Setting class), מייצגת הגדרה אחת בתוך SettingsGroup. ההודעה הזו מורכבת ממפתח, שהוא שם ההגדרה, ומערך, שיכול להיות אחד מכמה סוגים.

בדוגמה הזו אפשר לראות איך הגדרה מיוצגת באמצעות מפתח וערך:

// A key value pair representing a setting within a UserControllableService SettingsGroup.
message Setting {
  // Required.
  // A name that uniquely identifies a setting within a SettingsGroup.
  string key = 1;

  // Required.
  // New value of the setting.
  oneof value {
    bool bool = 2;
    float float = 3;
    int32 int32 = 4;
    int64 int64 = 5;
    bytes blob = 6;
    int32 enum = 7;
  }
}

הגדרות

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

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

// The definition of a setting, along with its properties such as type and constraints
message SettingDefinition {
  // Required.
  SettingKind kind = 1;

  // Required.
  SettingWithConstraints setting_with_constraints = 2;
}

enum SettingKind {
  // A setting which is applied for all vehicle users.
  SHARED = 0;
  // Store the value of the setting for each vehicle user separately.
  PER_USER = 1;
  // UserControllableService is fully responsible for the storage of PASSTHROUGH setting.
  // User Preferences only notifies UserControllableService when the value is explicitly set by
  // user. User Preferences does not attempt to request changes based on user change or service
  // registration.
  PASSTHROUGH = 2;
};

message SettingWithConstraints {
  // Required
  Setting setting = 1;

  // Required.
  // Defines restrictions on the setting's value
  oneof constraints {
    FloatConstraints float_constraints = 2;
    Int32Constraints int32_constraints = 3;
    Int64Constraints int64_constraints = 4;
    EnumConstraints enum_constraints = 5;
  }
}

message Int32Constraints {
  // The minimum value that a setting can have.
  optional int32 min_value = 1;
  // The maximum value that a setting can have.
  optional int32 max_value = 2;
  // The step by which a setting's value can be increased or decreased.
  optional int32 step = 3;
}

message Int64Constraints {
  // The minimum value that a setting can have.
  optional int64 min_value = 1;
  // The maximum value that a setting can have.
  optional int64 max_value = 2;
  // The step by which a setting's value can be increased or decreased.
  optional int64 step = 3;
}

message FloatConstraints {
  // The minimum value that a setting can have.
  optional float min_value = 1;
  // The maximum value that a setting can have.
  optional float max_value = 2;
  // The step by which a setting's value can be increased or decreased.
  optional float step = 3;
}

message EnumConstraints {
  // Required.
  // List of unique values sorted in ascending order.
  repeated int32 possible_values = 1;
}

דוגמה לרצף אירועים כשמשתמש מעדכן הגדרה

בדוגמה הזו מוצגת רצף האירועים שמתרחשים כשמשתמש משתמש במערכת המולטימדיה (IVI) של מערכת ההפעלה Android Automotive OS (AAOS) ברכב כדי לעדכן הגדרה:

אירועים שמתרחשים כשמשתמש משנה הגדרה

איור 1. אירועים שמתרחשים כשמשתמש משנה הגדרה.

דוגמה לרצף אירועים כשמניעים רכב

בדוגמה הזו אפשר לראות איך SDV User Preferences מחיל את ההגדרות המועדפות של המשתמש כשהרכב מתחיל לפעול:

אירועים כשהרכב מתחיל לפעול

איור 2. אירועים שמתרחשים כשהרכב מתניע.