Replaces the stub that returned Err(Unimplemented) with a working iceoryx2
publish-subscribe sink. Key design:
- One iceoryx2 pub/sub service per configured stream at {prefix}/{stream}
- Fixed-size [u8] samples with in-band binary header (seq, src_ts_us, ts_us,
NUL-padded key(48), len(32), value(N)) — no unsafe repr(packed) needed
- Heartbeat stream at {prefix}/hzbridge_heartbeat with same layout
- Uses BackpressureStrategy::DiscardData to avoid blocking the feeder
- Uses ipc_threadsafe::Service so the Publisher is Send (ipc::Service uses
SingleThreaded sync which is !Send)
- Daemon builds Iceoryx2Sink by default; --allow-null-sink for dev use only
Constraint compliance:
C1 — no unsafe (crate-root forbid(unsafe_code) enforced by compiler)
C2 — no async (all iceoryx2 calls synchronous)
C5 — iceoryx2 0.9.1 already pinned per ADR-001
C8 — fixed-size samples, pre-allocated publishers, bounded loan slots
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
88 lines
2.7 KiB
Rust
88 lines
2.7 KiB
Rust
use hzbridge::config::Config;
|
|
use hzbridge::sink::PlaneSink;
|
|
use std::path::PathBuf;
|
|
|
|
fn main() {
|
|
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("hzbridge=info"))
|
|
.format_timestamp_micros()
|
|
.init();
|
|
|
|
let arguments = match parse_arguments() {
|
|
Ok(a) => a,
|
|
Err(detail) => {
|
|
eprintln!("hzbridged: {detail}");
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
|
|
let config = match Config::from_path(&arguments.config_path) {
|
|
Ok(c) => c,
|
|
Err(error) => {
|
|
eprintln!("hzbridged: {error}");
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
|
|
let sink: Box<dyn PlaneSink> = build_sink(&config, arguments.allow_null_sink);
|
|
|
|
log::info!("fsm=Boot msg=starting name=hzbridge");
|
|
let bridge = hzbridge::Bridge::new(config, sink);
|
|
if let Err(error) = bridge.run() {
|
|
hzbridge::blackbox::abort(&error.to_string());
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "iceoryx2")]
|
|
fn build_sink(config: &Config, allow_null_sink: bool) -> Box<dyn PlaneSink> {
|
|
if allow_null_sink {
|
|
log::warn!("fsm=Boot msg=development_null_sink_enabled");
|
|
return Box::new(hzbridge::sink::NullSink);
|
|
}
|
|
match hzbridge::sink_iceoryx2::Iceoryx2Sink::new(config) {
|
|
Ok(sink) => {
|
|
log::info!("fsm=Boot msg=iceoryx2_sink_ready");
|
|
Box::new(sink)
|
|
}
|
|
Err(error) => {
|
|
log::error!("fsm=Boot msg=iceoryx2_sink_failed error={error}");
|
|
eprintln!("hzbridged: iceoryx2 sink failed: {error}");
|
|
std::process::exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "iceoryx2"))]
|
|
fn build_sink(_config: &Config, allow_null_sink: bool) -> Box<dyn PlaneSink> {
|
|
if !allow_null_sink {
|
|
eprintln!(
|
|
"hzbridged: iceoryx2 feature disabled and no --allow-null-sink; \
|
|
use --allow-null-sink only for wire/runtime smoke tests"
|
|
);
|
|
std::process::exit(1);
|
|
}
|
|
log::warn!("fsm=Boot msg=development_null_sink_enabled (iceoryx2 feature not available)");
|
|
Box::new(hzbridge::sink::NullSink)
|
|
}
|
|
|
|
struct Arguments {
|
|
config_path: PathBuf,
|
|
allow_null_sink: bool,
|
|
}
|
|
|
|
fn parse_arguments() -> Result<Arguments, String> {
|
|
let args: Vec<_> = std::env::args_os().skip(1).collect();
|
|
match args.as_slice() {
|
|
[flag, path] if flag == "--config" => Ok(Arguments {
|
|
config_path: path.into(),
|
|
allow_null_sink: false,
|
|
}),
|
|
[flag, path, allow] if flag == "--config" && allow == "--allow-null-sink" => {
|
|
Ok(Arguments {
|
|
config_path: path.into(),
|
|
allow_null_sink: true,
|
|
})
|
|
}
|
|
_ => Err("usage: hzbridged --config <path> [--allow-null-sink]".to_owned()),
|
|
}
|
|
}
|