El Servicio de configuración y calibración (ConCal) en la plataforma de vehículos definidos por software (SDV) proporciona capacidades para configurar servicios de SDV según las especificaciones del vehículo, las reglamentaciones del país y las funciones solicitadas por el cliente. Este servicio es un componente fundamental de la plataforma de SDV, que permite a los OEM reutilizar el mismo código de servicio en varios vehículos configurándolos y habilitando la reconfiguración desde varias fuentes (por ejemplo, en una fábrica, en un centro de servicio o desde la nube).
La plataforma de SDV proporciona APIs orientadas al servicio para la configuración y calibración de paquetes de servicios en un vehículo específico. Con esta interfaz, los OEM pueden implementar una lógica de configuración y calibración específica del OEM.
El servicio de configuración y calibración incluye los siguientes procesos:
Configuración, que implica definir las propiedades y el comportamiento fundamentales de un vehículo y puede depender de varios factores, como la ubicación del vehículo, las opciones solicitadas por el usuario o las reglamentaciones del país. Establece cómo interactúan los componentes y dicta la configuración del software que influye en la funcionalidad general del vehículo, como las variantes de software, las conexiones de red y los parámetros operativos iniciales.
Calibración, que ajusta los parámetros del sistema dentro de sus rangos preconfigurados. Por ejemplo, la calibración ajusta la precisión del sensor y del actuador, optimiza el rendimiento del motor para el control de emisiones y refina la capacidad de conducción y las respuestas del sistema de seguridad. La configuración establece el marco básico para el funcionamiento de un vehículo, mientras que la calibración optimiza su comportamiento dentro de ese marco. Ambos son fundamentales para garantizar que los vehículos cumplan con las reglamentaciones de emisiones, maximicen el rendimiento, mejoren la seguridad y puedan compensar el desgaste con el tiempo.
Al proporcionar una API de ConCal estándar en toda la SDV, simplificamos la implementación de paquetes de servicios de SDV, lo que evita la necesidad de volver a implementar las capacidades de configuración y calibración para que se ejecuten en diferentes vehículos de diferentes OEM.
Arquitectura
Cada paquete de servicios puede tener uno o más artefactos de configuración.
Artefactos de configuración
Un artefacto de configuración (config) consta de uno o más parámetros de configuración y sus valores. La configuración es un mensaje de protobuf específico del servicio cuyos campos pueden incluir mensajes de protobuf anidados (struct), mapas, arrays, int, float, bool, bytes o parámetros de cadena.
// 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;
}
Identificador de configuración
Cada configuración tiene un identificador único. Este identificador consta del nombre de instancia completamente calificado del propietario del paquete de servicios y un nombre de configuración. Un nombre de configuración debe ser legible por humanos, único por paquete de servicios,
y seguir los estándares de nombres definidos en
las convenciones de nombres de paquetes de servicios,
como shared, private, diagnostics, y calibration.
Restricciones:
- El nombre de la instancia debe comenzar con una letra.
- Todos los caracteres deben ser alfanuméricos en minúscula o un guion.
- Los guiones en el nombre no deben aparecer de forma consecutiva más de una vez.
- El nombre de la configuración no debe terminar con un guion.
- El nombre de la configuración no debe tener más de 48 caracteres.
- Los nombres de las configuraciones deben ser únicos en la misma VM para el mismo paquete de servicios.
Antes de 26Q2, el ID de configuración se define de la siguiente manera:
// 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;
}
El propietario de la configuración en el arranque solo conoce el esquema de su configuración y sus valores predeterminados. Para personalizar el comportamiento del paquete de servicios en un vehículo actual, el paquete de servicios propietario debe registrar su configuración predeterminada junto con su esquema.
Figura 1: Registro de la configuración predeterminada y recuperación de la configuración personalizada
Esto permite que los OEM implementen paquetes de servicios una vez y los ejecuten en varios vehículos.
Implementación
ConCal puede mantener una o más instancias del servidor en la plataforma de SDV. Los paquetes de servicios deben descubrir y usar el servidor ConCal más cercano. Por ejemplo, si ConCal se implementa uno por ECU, un paquete de servicios debe obtener acceso a la instancia de ConCal que se ejecuta en la misma ECU. Esto permite que el paquete de servicios recupere la configuración de manera oportuna. Si la instancia de ConCal no tiene una configuración solicitada (ya que pertenece al área de responsabilidad de otro ConCal), el servidor ConCal contactado la solicita a la instancia de ConCal propietaria y la redirecciona al paquete de servicios.
Personalización de la configuración
La capacidad de reutilizar el mismo software con diferentes vehículos es una de las principales ventajas de SDV. El software se desarrolla una vez y, luego, se reutiliza en varios vehículos, y podemos ajustar el comportamiento del software según las especificaciones del vehículo. Este es el objetivo principal de ConCal, que calcula la configuración del servicio en función de las propiedades del vehículo con la ayuda de anulaciones de configuración.
ConfigOverride es un mensaje de protobuf que describe cómo ajustar la configuración al vehículo específico. Consta de un ID de anulación, que se define de forma única por la entidad que lo proporciona, un identificador de configuración y una lista de ConfigOverrideKeyValuePair. ConfigOverride solo se puede proporcionar durante el proceso de actualización y solo por los servicios permitidos, que modela el OEM. A continuación, se proporcionan las definiciones de protobuf para ambas estructuras.
// 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 admite estas operaciones:
Asigna un valor nuevo: El último valor dejó de estar disponible y al parámetro se le asigna un valor nuevo, como asignar un valor nuevo a un campo simple (int, string, float, bool, bytes) o reescribir campos complejos, como mapas, listas, structs o una configuración completa.
Quita o limpia un valor: Esta operación se proporciona para todos los tipos, incluidos los mensajes, los campos repetidos, los mapas, los campos singulares y la configuración en sí. Esta operación se puede realizar en un campo o mensaje completo, lo que significa que no se admite la eliminación de claves específicas en un mapa y elementos individuales de un campo repetido.
Agrega un valor nuevo a un mapa.
Reescribe el valor de una clave existente en un mapa (esta operación también se puede realizar como agregar un valor nuevo a un mapa, si la clave no existe).
Configura un paquete de servicios con ConCal
En este capítulo, se describe cómo desarrollar un paquete de servicios que recupera su configuración en el tiempo de ejecución. Desde una perspectiva de paquete configurable, es completamente opaco si la configuración recuperada es un parámetro de configuración predeterminado de fábrica o una modificación posterior con los procesos de anulación y calibración de ConCal.
Puedes encontrar la muestra en la que se basa la documentación en system/software_defined_vehicle/samples/concal/src/concal_client.
Consulta Desarrollo de paquetes de servicios para obtener más detalles.
Declara el tipo de configuración que posee el paquete de servicios
El paquete de servicios posee el tipo de configuración que recupera. Esto permite que un paquete configurable se actualice de forma independiente de los datos de configuración persistentes.
Escribe un archivo protobuf (con una extensión
.proto) que declare el tipo de configuració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; }Crea un destino de compilación que genere una biblioteca de tiempo de ejecución que permita la recuperación del tipo de configuración. En un archivo
Android.bp: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", }
Genera código de middleware de RPC de ConCal para tu paquete
Agrega una declaración de VSIDL al paquete de servicios:
package: "com.sdv.oem.sample.concal"
service_bundle {
name: "SampleOemConCalClientServiceBundle"
client {
service: "com.sdv.google.concal.ConCalRegistrationService"
}
}
Esto declara que el paquete es un cliente del servicio de registro y recuperación de configuración de ConCal. Consulta Descripción general de VSIDL y middleware para obtener una descripción general del uso de VSIDLC para generar vinculaciones de cliente RPC para el paquete.
Inicializa el middleware para la recuperación de la configuración de RPC
En el tiempo de ejecución, inicializa los componentes de middleware necesarios para las llamadas RPC de ConCal. En este ejemplo, inicializamos de forma asíncrona cuando se inicia el paquete.
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")
}
En la que:
- En
on_start, creamos un tiempo de ejecución de Tokio y generamos una tarea de Tokio. La tarea llama asetup_register_rpcantes de continuar con la lógica principal del paquete. setup_register_rpcconfigura la vinculación de RPC de middleware. Ten en cuenta que el ejemplo no supone que un agente implemente la funcionalidad de ConCal: es posible que el servidor esté disponible solo después de que se haya iniciado el paquete. Por lo tanto, el código de ejemplo espera que se registre el servidor RPC con la API de Service Discovery.
Inicializa la configuración
Para registrar el artefacto de configuración, proporciona al servidor ConCal un ID, el esquema de configuración y los valores de configuración predeterminados y de fábrica.
Si se llama al registro por primera vez, se conserva el valor predeterminado. Si se llama al registro en los inicios posteriores del paquete, se recuperará el valor predeterminado persistente con las anulaciones de ConCal aplicadas (si las hay).
La llamada de configuración de registro de ConCal nunca falla, independientemente de si el artefacto ya está registrado.
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",
);
}
En la que:
- La tarea generada en
on_start, después de recuperar la vinculación de RPC, continúa con la lógica empresarial y llama asample_concal_main. sample_concal_maincomienza registrando un artefacto de configuración. La lógica se encuentra enregister_config.- Para registrar una configuración, un paquete debe especificar su tipo de protobuf, un ID y un valor predeterminado.
config_fdes el tipo de configuración. El paquete que posee el tipo de configuración garantiza que siempre se recupere el esquema que espera el paquete, incluidas las actualizaciones posteriores a APEX.- El ID se usa como identificador en la lógica de persistencia interna del servidor ConCal.
- El valor predeterminado se construye en
get_rear_view_camera_factory_config. El valor predeterminado es el valor que conserva el servidor ConCal, si no se conservó nada antes. Esto representa una de las formas en que el sistema puede especificar configuraciones de fábrica. Son posibles otras configuraciones.
Recupera la configuración de
Después del registro, recupera la configuración. Como la configuración se registró antes, se garantiza que esta llamada se realizará correctamente.
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")
}
En la que:
Se garantiza que la llamada a
GetConfigRequestse realizará correctamente, ya que la configuración se registró antes, con el mismo paquete. La configuración permite que el paquete de servicios controle de forma opaca ambos casos de configuraciones de fábrica y configuraciones anuladas: la lógica empresarial del paquete de servicios permanece sin cambios.La llamada
GetConfigRequestmuestra bytes sin procesar. La funciónget_configcontinúa con el análisis sintáctico para el tipo de configuración esperado.