extern crate std; use std::vec::Vec; use kaptan_config::{Config, ConfigModel, ConfigValue, FieldId}; use kaptan_ports::{PortCatalog, PortDescriptor, PortSettings, Ports, Role}; use platform_stm32f7::{ f722::{F722Route, Icm42688Imu, ImuService, Mpu6000Imu}, *, }; use crate::f722::{config as target_config, geprc, kakute, ports}; fn board_route() where B: F7Board, B::ImmutableConfig: F7ImmutableConfig, R: F722Route, Imu: ImuService, { } #[test] fn board_defaults_disable_configurable_ports() { board_route::(); board_route::(); fn check() { let candidate = target_config::UserConfigModel::<_, B::ImmutableConfig, 5>::new(B::FACTORY_USER_CONFIG); let active = candidate.validate(&B::IMMUTABLE_CONFIG).unwrap(); let inventory = B::IMMUTABLE_CONFIG.f722().hardware.serial(); assert_eq!(inventory.len(), 5); assert_eq!( active .ports .iter() .map(|(id, _)| id.get()) .collect::>(), [1, 2, 3, 4, 6] ); assert!( active .ports .iter() .all(|(_, port)| port.role == Role::Disabled) ); let fields: Vec<_> = (0..B::UserConfig::FIELD_COUNT) .map(|index| B::UserConfig::descriptor(index).unwrap()) .collect(); assert_eq!(fields.len(), 7 + 2 * inventory.len()); assert_eq!( fields[..7] .iter() .map(|field| field.id.get()) .collect::>(), [101, 102, 105, 103, 104, 106, 107] ); for (port, fields) in inventory .descriptors() .iter() .zip(fields[7..].chunks_exact(2)) { assert_eq!(fields, port.fields().iter().collect::>()); assert_eq!( candidate.field_value(port.baud_field), Some(ConfigValue::U32(115_200)) ); } assert_eq!(inventory.index(kaptan_ports::PortId::new(0)), None); } check::(); check::(); } struct Both; impl PortCatalog<3> for Both { const DESCRIPTORS: [PortDescriptor; 3] = [UART1, UART2, UART4]; } #[test] fn independent_imu_serial_and_led_facts_allow_cross_route_compositions() { let mpu = Mpu6000Spi1 { mount: ImuMount::BodyAligned, config: Mpu6000Config { accelerometer: AccelerometerRange::PlusMinus4G, gyroscope: GyroscopeRange::PlusMinus500DegreesPerSecond, digital_low_pass_filter: DigitalLowPassFilter::Bandwidth42Hz, sample_rate_divider: 9, }, }; let icm = Icm42688Spi3 { mount: ImuMount::Yaw180, }; fn check(route: R) { for led in [StatusLed::ActiveLowPd2, StatusLed::ActiveHighPa13] { let inventory = SerialInventory::new(&Both::DESCRIPTORS); let hardware = F722Hardware::new(route, inventory, led, VerticalSensor::None, MotorPins::Four); for sensor in [0, 1, 2] { let mut settings = [PortSettings { role: Role::Mavlink, baud: 115_200, }; 3]; settings[sensor].role = Role::Mtf01; let root = Ports::::new(settings); let plan = ports::project_ports::<_, 3>(&root, hardware.serial(), 3400).unwrap(); assert_eq!( plan.iter().filter(|(_, p)| p.role == Role::Mavlink).count(), 2 ); assert_eq!( plan.iter().filter(|(_, p)| p.role == Role::Mtf01).count(), 1 ); } } } check(mpu); check(icm); } #[test] fn double_sensor_unsupported_baud_and_missing_inventory_fields_are_rejected() { let inventory = SerialInventory::new(&Both::DESCRIPTORS); let mut root = Ports::::new( [PortSettings { role: Role::Mtf01, baud: 115_200, }; 3], ); let error = ports::project_ports::<_, 3>(&root, inventory, 0).unwrap_err(); assert_eq!(error.primary(), Some(UART2.role_field)); assert_eq!(error.related_field(), Some(UART1.role_field)); root.set_field(FieldId::new(203), ConfigValue::U8(0)) .unwrap(); root.set_field(FieldId::new(207), ConfigValue::U8(0)) .unwrap(); assert!(ports::project_ports::<_, 3>(&root, inventory, 0).is_ok()); assert!( root.set_field(FieldId::new(202), ConfigValue::U32(12345)) .is_err() ); root.set_field(FieldId::new(202), ConfigValue::U32(230400)) .unwrap(); assert!(ports::project_ports::<_, 3>(&root, inventory, 0).is_err()); root.set_field(FieldId::new(201), ConfigValue::U8(1)) .unwrap(); assert!(ports::project_ports::<_, 3>(&root, inventory, 23041).is_err()); assert!( ports::project_ports::<_, 3>(&geprc::GeprcF722V2::FACTORY_USER_CONFIG, inventory, 0) .is_err() ); } #[test] fn schema_inventory_and_storage_count_must_agree_even_when_disabled() { let root = Ports::::new( [PortSettings { role: Role::Disabled, baud: 115_200, }; 3], ); // No silent unused fields, including fields whose current role is Disabled. assert!(ports::project_ports::<_, 1>(&root, SerialInventory::new(&[UART1]), 0).is_err()); assert!(ports::project_ports::<_, 0>(&root, SerialInventory::new(&[]), 0).is_err()); assert!( ports::project_ports::<_, 2>(&root, SerialInventory::new(&Both::DESCRIPTORS), 0).is_err() ); assert!( ports::project_ports::<_, 3>(&root, SerialInventory::new(&[UART1, UART1, UART2]), 0) .is_err() ); } #[test] fn rejects_invalid_port_descriptors() { let root = Ports::::new( [PortSettings { role: Role::Disabled, baud: 115_200, }; 3], ); const DISABLED_ROLES: [(u8, &str); 1] = [Role::CONFIG_ENUM_OPTIONS[0]]; const INVALID_INVENTORIES: [[PortDescriptor; 3]; 6] = { let mut rows = [[UART1, UART2, UART4]; 6]; rows[0][0].id = kaptan_ports::PortId::new(3); rows[1][0].resources = &[8, 9, 101]; rows[2][0].role_field = FieldId::new(209); rows[3][0].role_key = "ports.other.role"; rows[4][0].allowed_bauds = &[9600]; rows[5][0].supported_roles = &DISABLED_ROLES; rows }; for descriptors in &INVALID_INVENTORIES { let inventory = SerialInventory::new(descriptors); assert!(ports::project_ports::<_, 3>(&root, inventory, 0).is_err()); } struct WrongMetadata; impl PortCatalog<1> for WrongMetadata { const DESCRIPTORS: [PortDescriptor; 1] = [PortDescriptor { allowed_bauds: &[9600], ..UART1 }]; } let root = Ports::::new([PortSettings { role: Role::Disabled, baud: 9600, }]); assert!(ports::project_ports::<_, 1>(&root, SerialInventory::new(&[UART1]), 0).is_err()); } #[test] fn stable_ids_follow_catalog_order_and_disabled_ports_keep_their_slots() { struct Reordered; impl PortCatalog<2> for Reordered { const DESCRIPTORS: [PortDescriptor; 2] = [UART4, UART1]; } let inventory = SerialInventory::new(&Reordered::DESCRIPTORS); let mut root = Ports::::new( [PortSettings { role: Role::Disabled, baud: 115_200, }; 2], ); for role in [Role::Disabled, Role::Mavlink, Role::Mtf01] { root.set_field(UART4.role_field, ConfigValue::U8(role.wire_value())) .unwrap(); let plan = ports::project_ports::<_, 2>(&root, inventory, 0).unwrap(); assert_eq!( plan.iter().map(|(id, _)| id.get()).collect::>(), [4, 1] ); assert_eq!(plan.get(UART4.id).unwrap().role, role); assert_eq!(inventory.index(UART4.id), Some(0)); assert_eq!(inventory.index(UART1.id), Some(1)); for id in [0, 2, 3, 5, 255] { assert_eq!(inventory.index(kaptan_ports::PortId::new(id)), None); } } }