Yazılımla tanımlanan araç (SDV) platformundaki Yapılandırma ve Kalibrasyon Hizmeti (ConCal), SDV hizmetlerini araç özelliklerine, ülke yönetmeliklerine ve müşterinin sipariş ettiği özelliklere göre yapılandırma imkanı sunar. Bu hizmet, SDV platformunun temel yapı taşıdır. OEM'lerin, yapılandırarak ve birden fazla kaynaktan (ör. fabrikada, servis merkezinde, buluttan) yeniden yapılandırmayı etkinleştirerek aynı hizmet kodunu birden fazla araçta yeniden kullanmasına olanak tanır.
SDV platformu, belirli bir araçtaki hizmet paketlerinin yapılandırılması ve kalibrasyonu için hizmete yönelik API'ler sağlar. OEM'ler bu arayüzü kullanarak OEM'e özgü yapılandırma ve kalibrasyon mantığı uygulayabilir.
Yapılandırma ve Kalibrasyon hizmeti aşağıdaki süreçleri içerir:
Yapılandırma: Bir aracın temel özelliklerini ve davranışını tanımlamayı içerir. Aracın konumu, kullanıcının sipariş ettiği seçenekler veya ülke yönetmelikleri gibi birden fazla faktöre bağlı olabilir. Bileşenlerin nasıl etkileşim kurduğunu belirler ve yazılım varyantları, ağ bağlantıları ve ilk operasyonel parametreler gibi aracın genel işlevselliğini etkileyen yazılım ayarlarını belirler.
Kalibrasyon: Sistem parametrelerini önceden yapılandırılmış aralıklarında hassas bir şekilde ayarlar. Örneğin, kalibrasyon sensör ve aktüatör doğruluğunu ayarlar, emisyon kontrolü için motor performansını optimize eder, sürüş kolaylığını ve güvenlik sistemi yanıtlarını iyileştirir. Yapılandırma, bir aracın işleyişiyle ilgili temel çerçeveyi belirlerken kalibrasyon, bu çerçeve içindeki davranışını optimize eder. Her ikisi de araçların emisyon yönetmeliklerine uymasını, performansı en üst düzeye çıkarmasını, güvenliği artırmasını ve zaman içinde aşınma ve yıpranmayı telafi etmesini sağlamak için kritik öneme sahiptir.
Standart bir SDV genelinde ConCal API'si sağlayarak SDV hizmet paketlerinin uygulanmasını basitleştiriyoruz. Böylece, farklı OEM'lerin farklı araçlarında çalışmak için yapılandırma ve kalibrasyon özelliklerinin yeniden uygulanmasına gerek kalmıyor.
Mimari
Her hizmet paketi, bir veya daha fazla yapılandırma yapıtına sahip olabilir.
Yapılandırma yapıları
Yapılandırma yapıtı (yapılandırma), bir veya daha fazla yapılandırma parametresinden ve bunların değerlerinden oluşur. Yapılandırma, alanları iç içe yerleştirilmiş protobuf iletilerini (struct), eşlemeleri, dizileri, int, float, bool, bayt veya dize parametrelerini içerebilen hizmete özgü bir protobuf iletisidir.
// Example of a configuration message.
message SampleServiceBundleConfig
{
bool bool_parameter = 1;
int64 int_parameter = 2;
float float_parameter = 3;
string str_parameter = 4;
repeated string list_parameter = 5;
map<string, int32> map_parameter = 6;
SomeNestedMessage nested_parameter = 7;
SomeComplexMessage complex_parameter = 8;
some.nested.package.SomeNestedMessage nested_package = 9;
bytes bytes_parameter = 10;
}
Yapılandırma tanımlayıcısı
Her yapılandırmanın benzersiz bir tanımlayıcısı vardır. Bu tanımlayıcı, hizmet paketi sahibinin tam nitelikli örnek adından ve bir yapılandırma adından oluşur. Yapılandırma adı, insanlar tarafından okunabilir olmalı, hizmet paketi başına benzersiz olmalı ve Hizmet paketi adlandırma kuralları'nda tanımlanan adlandırma standartlarına (ör. shared, private, diagnostics ve calibration) uygun olmalıdır.
Kısıtlamalar:
- Örnek adı bir harfle başlamalıdır.
- Tüm karakterler küçük alfanümerik veya tire olmalıdır.
- Addaki kısa çizgiler arka arkaya birden fazla kez görünmemelidir.
- Yapılandırma adı kısa çizgiyle bitmemelidir.
- Yapılandırma adı 48 karakterden uzun olmamalıdır.
- Yapılandırma adları, aynı hizmet paketi için aynı sanal makinede benzersiz olmalıdır.
26Q2'den önce yapılandırma kimliği şu şekilde tanımlanır:
// Unique identifier for the config.
message ConfigId {
// The FQIN of the service bundle that owns the configuration.
com.sdv.google.sd_common.ServiceFqin service_fqin = 1;
// The name of the config.
string config_name = 2;
}
Önyükleme sırasında yapılandırma sahibi yalnızca yapılandırmasının şemasını ve varsayılan değerlerini bilir. Hizmet paketi davranışını mevcut bir araca göre özelleştirmek için sahip olunan hizmet paketinin şemasıyla birlikte varsayılan yapılandırmasını kaydetmesi gerekir.
Şekil 1. Varsayılan yapılandırmanın kaydedilmesi ve özelleştirilmiş yapılandırmanın alınması.
Bu sayede OEM'ler, hizmet paketlerini bir kez uygulayıp birden fazla araçta çalıştırabilir.
Dağıtım
ConCal, SDV platformunda bir veya daha fazla sunucu örneği bulundurabilir. Hizmet paketleri, en yakın ConCal sunucusunu keşfedip kullanmalıdır. Örneğin, ConCal her ECU için bir kez dağıtılıyorsa bir hizmet paketi, aynı ECU'da çalışan ConCal örneğine erişmelidir. Bu sayede hizmet paketi, yapılandırmayı zamanında alabilir. ConCal örneğinde istenen yapılandırma yoksa (başka bir ConCal'ın sorumluluk alanına ait olduğu için) iletişime geçilen ConCal sunucusu, bu yapılandırmayı sahibi olan ConCal örneğinden ister ve hizmet paketine yönlendirir.
Yapılandırmayı özelleştirme
Aynı yazılımı farklı araçlarda yeniden kullanabilme, SDV'nin temel avantajlarından biridir. Yazılım bir kez geliştirilir ve ardından birden fazla araçta yeniden kullanılır. Ayrıca, yazılımın davranışını araca özgü özelliklere göre ayarlayabiliriz. Bu, yapılandırma geçersiz kılmaları yardımıyla hizmet yapılandırmasını araç özelliklerine göre hesaplayan ConCal'ın temel amacıdır.
ConfigOverride, yapılandırmanın belirli bir araca nasıl ayarlanacağını açıklayan bir protobuf mesajıdır. Bu, sağlayan kuruluş tarafından benzersiz şekilde tanımlanan bir geçersiz kılma kimliği, bir yapılandırma tanımlayıcısı ve bir ConfigOverrideKeyValuePair listesinden oluşur. ConfigOverride yalnızca güncelleme işlemi sırasında ve yalnızca OEM tarafından modellenen izin verilen hizmetler tarafından sağlanabilir. Her iki yapı için protobuf tanımları aşağıda verilmiştir.
// Key-value pair to update configuration.
message ConfigOverrideKeyValue {
string key = 1;
oneof value {
string value_txtproto = 2;
.google.protobuf.Any value_any = 3;
}
}
// A collection of changes for a specific configuration which should be atomically applied.
message ConfigOverride {
string override_id = 1;
ConfigId config_id = 2;
repeated ConfigOverrideKeyValue pairs = 3;
}
ConfigOverride aşağıdaki işlemleri destekler:
Yeni bir değer atama: Son değer kullanımdan kaldırılır ve parametreye yeni bir değer atanır. Örneğin, basit bir alana (int, string, float, bool, bytes) yeni bir değer atanır veya haritalar, listeler, yapılar ya da eksiksiz bir yapılandırma gibi karmaşık alanlar yeniden yazılır.
Değeri kaldırma veya temizleme: Bu işlem, iletiler, tekrarlanan alanlar, haritalar, tekil alanlar ve yapılandırmanın kendisi dahil olmak üzere tüm türler için sağlanır. Bu işlem, tam bir alan veya mesaj üzerinde gerçekleştirilebilir. Bu nedenle, bir haritadaki belirli anahtarların ve yinelenen bir alandaki tek tek öğelerin kaldırılması desteklenmez.
Haritaya yeni bir değer ekleyin.
Bir haritadaki mevcut anahtarın değerini yeniden yazma (bu işlem, anahtar yoksa haritaya yeni bir değer ekleme olarak da gerçekleştirilebilir).
ConCal kullanarak hizmet paketi yapılandırma
Bu bölümde, yapılandırmasını çalışma zamanında alan bir hizmet paketi geliştirme açıklanmaktadır. Yapılandırılabilir paket açısından, alınan yapılandırmanın varsayılan fabrika ayarı mı yoksa ConCal geçersiz kılma ve kalibrasyon işlemleri kullanılarak yapılan sonraki bir değişiklik mi olduğu tamamen belirsizdir.
Belgelerin temel alındığı örneği system/software_defined_vehicle/samples/concal/src/concal_client adresinde bulabilirsiniz.
Daha fazla bilgi için Hizmet paketi geliştirme bölümüne bakın.
Hizmet paketinin sahip olduğu yapılandırma türünü bildirin.
Hizmet paketi, aldığı yapılandırmanın türüne sahiptir. Bu, yapılandırılabilir paketin kalıcı yapılandırma verilerinden bağımsız olarak güncellenmesine olanak tanır.
Yapılandırma türünü bildiren bir protobuf dosyası (
.protouzantılı) yazın:syntax = "proto3"; package android.sdv.demo.config; message RearViewCamera { string model = 1; uint64 horizontal_resolution = 2; uint64 vertical_resolution = 3; float x_axis_field_of_view = 4; float y_axis_field_of_view = 5; bool is_rgb = 6; }Yapılandırma türü alımına izin veren bir çalışma zamanı kitaplığı oluşturan bir derleme hedefi oluşturun.
Android.bpdosyasında:rust_protobuf { name: "libsdvtestconcal_proto_rust", crate_name: "sdvtestconcal_proto_rust", protos: [ "rear_view_camera.proto", ], proto_flags: [ "-I external/protobuf/src", "-I .", ], source_stem: "sdvtestconcal_proto_rust", vendor_available: true, product_available: true, min_sdk_version: "35", }
Paketiniz için ConCal RPC ara katman yazılımı kodu oluşturma
Hizmet paketine VSIDL beyanı ekleyin:
package: "com.sdv.oem.sample.concal"
service_bundle {
name: "SampleOemConCalClientServiceBundle"
client {
service: "com.sdv.google.concal.ConCalRegistrationService"
}
}
Bu, paketin ConCal yapılandırma kaydı ve alma hizmetinin istemcisi olduğunu belirtir. Paket için RPC istemci bağlamaları oluşturmak üzere VSIDLC'yi kullanmaya genel bakış için VSIDL ve ara katman yazılımına genel bakış başlıklı makaleyi inceleyin.
RPC yapılandırması alma için ara yazılımı başlatma
Çalışma zamanında, ConCal RPC çağrıları için gereken ara yazılım bileşenlerini başlatın. Bu örnekte, paket başlatıldığında eşzamansız olarak başlatma işlemi yapıyoruz.
pub struct ExampleConcalBundle {
context: ContextRef,
runtime: Option<Runtime>,
}
sdv::lifecycle::register_service_bundle!(ExampleConcalBundle);
impl ServiceBundle for ExampleConcalBundle {
fn new(context: ContextRef) -> ExampleConcalBundle {
info!("Creating {}.", context.get_self_fqin());
ExampleConcalBundle { context, runtime: None }
}
fn on_start(&mut self) {
let fqin = self.context.get_self_fqin();
info!("Starting {}.", fqin);
let runtime = Builder::new_multi_thread()
.worker_threads(4)
.thread_name("tokio-pool")
.enable_all()
.build()
.unwrap();
let context = self.context;
runtime.spawn(async move {
let registration_client = setup_register_config_rpc(context).await;
/* main SB logic here */
});
self.runtime = Some(runtime);
}
async fn setup_register_config_rpc(context: ContextRef) -> RegistrationClient {
let sdv_comms = SdvComms { context };
let sd = ServiceDiscoveryManager::new(context);
let unit_name_args = UnitNameDiscoveryArgs::new_builder()
.set_sdv_package_name("com.sdv.oem.sample.concal")
.set_service_bundle_name("SampleOemConCalServiceBundle")
.set_service_unit_name(RegistrationClient::DEFAULT_UNIT_NAME)
.build()
.unwrap();
let mut unit_name_stream =
sd.subscribe_service_unit_change_by_name(&unit_name_args).await.unwrap();
// wait until RPC servers are registered, if server is not a custom agent
while let Some(event) = unit_name_stream.next().await {
if let ServiceUnitChangeEvent::Registered(sud) = event {
let service_identity = sud.get_service_bundle_identity();
let fqin = service_identity.get_fqin();
if fqin.get_sdv_package_name() == "com.sdv.oem.sample.concal"
&& fqin.get_service_bundle_name() == "SampleOemConCalServiceBundle"
&& fqin.get_service_instance_name() == "default"
{
break;
}
}
}
let service_bundle =
SampleOemConCalClientServiceBundle::new(Arc::new(sdv_comms)).await.unwrap();
service_bundle
.create_rpc_client::<RegistrationClient>(
UnitName::builder()
.package_name("com.sdv.oem.sample.concal")
.bundle_name("SampleOemConCalServiceBundle")
.service_unit_name(RegistrationClient::DEFAULT_UNIT_NAME)
.build()
.unwrap(),
ClientOptions::default(),
)
.await
.expect("Failed to create an RPC client")
}
Nerede:
on_startiçinde bir tokio çalışma zamanı oluşturup bir tokio görevi başlatıyoruz. Paketin ana mantığına geçmeden önce görevsetup_register_rpcçağrıları yapılır.setup_register_rpc, ara katman yazılımı RPC bağlamasını ayarlar. Örnekte, ConCal işlevinin bir aracı tarafından uygulandığı varsayılmadığını unutmayın: Sunucu, yalnızca paket başlatıldıktan sonra kullanılabilir hale gelebilir. Bu nedenle, örnek kod, RPC sunucusunun Service Discovery API kullanılarak kaydedilmesini bekler.
Yapılandırmayı başlatma
ConCal sunucusuna bir kimlik, yapılandırma şeması ve varsayılan fabrika yapılandırma değerleri sağlayarak yapılandırma yapıtını kaydedin.
Kayıt ilk kez çağrılıyorsa varsayılan değer kalıcı hale getirilir. Kayıt, sonraki paket başlatmalarında çağrılırsa, geçerli kılınan ConCal geçersiz kılmaları (varsa) ile birlikte kalıcı varsayılan değer alınır.
ConCal kayıt yapılandırma çağrısı, yapının zaten kayıtlı olup olmadığına bakılmaksızın hiçbir zaman başarısız olmaz.
impl ServiceBundle for ExampleConcalBundle{
/* ... */
fn on_start(&mut self) {
/* ... */
runtime.spawn(async move {
let registration_client = setup_register_config_rpc(context).await;
sample_concal_main(fqin, registration_client).await
});
self.runtime = Some(runtime);
}
}
async fn sample_concal_main(
fqin: ServiceFqin,
registration_client: RegistrationClient,
) -> sdv::status::SdvResult<()> {
let config = get_rear_view_camera_factory_config();
let config_id = get_config_id(&fqin);
register_config(®istration_client, &config_id, &config).await;
/* ... */
}
fn get_rear_view_camera_factory_config() -> RearViewCamera {
RearViewCamera {
model: String::from("model 1"),
horizontal_resolution: 720,
vertical_resolution: 720,
x_axis_field_of_view: 70.0,
y_axis_field_of_view: 70.0,
is_rgb: false,
..Default::default()
}
}
fn get_config_id(fqin: &ServiceFqin) -> ConfigId {
ConfigId {
config_name: "config".to_string(),
service_fqin: MessageField::some(ProtoFqin {
vm_name: fqin.get_sdv_vm_name().to_string(),
package_name: fqin.get_sdv_package_name().to_string(),
service_name: fqin.get_service_bundle_name().to_string(),
instance_name: fqin.get_service_instance_name().to_string(),
..Default::default()
}),
..Default::default()
}
}
async fn register_config(
client: &RegistrationClient,
config_id: &ConfigId,
config: &RearViewCamera,
) {
let config_fd = FileDescriptorSet {
file: vec![RearViewCamera::descriptor().file_descriptor_proto().clone()],
..Default::default()
};
let config = Any::pack(config).expect("Failed to pack config");
let config_metadata = ConfigMetadata {
descriptor_set: MessageField::some(config_fd),
default_config: MessageField::some(config.clone()),
..Default::default()
};
client
.RegisterConfigMetadata(&RegisterConfigMetadataRequest {
config_id: MessageField::some(config_id.clone()),
metadata: MessageField::some(config_metadata),
config_version: String::from("1.0"),
..Default::default()
})
.await
.expect(
"RegisterConfigMetadata should not fail, even if configuration was registered before",
);
}
Nerede:
- RPC bağlaması alındıktan sonra
on_startiçinde oluşturulan görev,sample_concal_mainçağrısı yaparak iş mantığıyla devam eder. sample_concal_main, yapılandırma yapay nesnesi kaydedilerek başlar. Mantıkregister_configiçinde yer alıyor.- Bir yapılandırmayı kaydetmek için paketin protobuf türünü, kimliğini ve varsayılan değerini belirtmesi gerekir.
config_fd, yapılandırma türüdür. Yapılandırma türüne sahip paket, APEX güncellemeleri sonrası da dahil olmak üzere paket tarafından beklenen şemanın her zaman alınmasını sağlar.- Kimlik, ConCal sunucusunun dahili kalıcılık mantığında tanımlayıcı olarak kullanılır.
- Varsayılan değer
get_rear_view_camera_factory_configiçinde oluşturulur. Daha önce hiçbir şey kalıcı hale getirilmemişse varsayılan değer, ConCal sunucusu tarafından kalıcı hale getirilen değerdir. Bu, sistemin fabrika yapılandırmalarını belirleyebileceği yollardan birini gösterir. Başka kurulumlar da mümkündür.
Yapılandırmayı alma
Kaydettikten sonra yapılandırmayı alın. Yapılandırma daha önce kaydedildiği için bu aramanın başarılı olacağı garanti edilir.
async fn sample_concal_main(
fqin: ServiceFqin,
registration_client: RegistrationClient,
update_client: UpdateClient,
) -> sdv::status::SdvResult<()> {
let config = get_rear_view_camera_factory_config();
let config_id = get_config_id(&fqin);
register_config(®istration_client, &config_id, &config).await;
let config = get_config(®istration_client, &config_id).await;
info!("Retrieved configuration:\n{config:#?}");
// Onwards, use configuration in bundle's main business logic
/* ... */
}
async fn get_config(client: &RegistrationClient, config_id: &ConfigId) -> RearViewCamera {
let bytes = client
.GetConfig(&GetConfigRequest {
config_id: MessageField::some(config_id.clone()),
..Default::default()
})
.await
.expect("Get config does not fail, as config was registered before")
.config;
RearViewCamera::parse_from_bytes(&bytes).expect("parse_from_bytes failed")
}
Nerede:
Aynı paket tarafından daha önce yapılandırma kaydedildiği için
GetConfigRequestçağrısının başarılı olacağı garanti edilir. Kurulum, hizmet paketinin hem fabrika yapılandırmaları hem de geçersiz kılınan yapılandırmalarla ilgili durumları şeffaf bir şekilde ele almasına olanak tanır. Hizmet paketi iş mantığı değişmeden kalır.Arama
GetConfigRequestham bayt döndürüyor. İşlev,get_configbeklenen yapılandırma türüne ayrıştırmaya devam eder.