use super::build::{
broadcast_to_peers, build_attitude_status_data, build_gimbal_manager_status_data,
};
use crate::daemon::attitude::AttitudeTick;
use crate::daemon::config::MavlinkConfig;
use crate::daemon::mavlink_manager::Peers;
use crate::daemon::state::StateManager;
use mavlink::ardupilotmega::{
GimbalDeviceErrorFlags, MavAutopilot, MavMessage, MavModeFlag, MavState, MavType,
HEARTBEAT_DATA,
};
use std::sync::atomic::AtomicU8;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::net::UdpSocket;
use tokio::sync::broadcast;
use tokio::time;
use tracing::warn;
pub(in crate::daemon::mavlink_manager) async fn heartbeat_loop(
socket: Arc<UdpSocket>,
peers: Peers,
seq: Arc<AtomicU8>,
config: MavlinkConfig,
) {
let mut interval = time::interval(Duration::from_secs(1));
loop {
interval.tick().await;
let hb = HEARTBEAT_DATA {
custom_mode: 0,
mavtype: MavType::MAV_TYPE_GIMBAL,
autopilot: MavAutopilot::MAV_AUTOPILOT_INVALID,
base_mode: MavModeFlag::empty(),
system_status: MavState::MAV_STATE_ACTIVE,
mavlink_version: 3,
};
broadcast_to_peers(
&socket,
&peers,
&config,
&seq,
MavMessage::HEARTBEAT(hb),
"HEARTBEAT",
)
.await;
}
}
pub(in crate::daemon::mavlink_manager) async fn publish_status_loop(
socket: Arc<UdpSocket>,
peers: Peers,
seq: Arc<AtomicU8>,
state_manager: StateManager,
config: MavlinkConfig,
start_time: Instant,
) {
let mut interval = time::interval(Duration::from_millis(200));
loop {
interval.tick().await;
let msg = MavMessage::GIMBAL_MANAGER_STATUS(build_gimbal_manager_status_data(
&state_manager,
&config,
start_time,
));
broadcast_to_peers(&socket, &peers, &config, &seq, msg, "GIMBAL_MANAGER_STATUS").await;
}
}
pub(in crate::daemon::mavlink_manager) async fn attitude_broadcast_loop(
socket: Arc<UdpSocket>,
peers: Peers,
seq: Arc<AtomicU8>,
config: MavlinkConfig,
start_time: Instant,
mut attitude_rx: broadcast::Receiver<AttitudeTick>,
) {
loop {
let tick = match attitude_rx.recv().await {
Ok(t) => t,
Err(broadcast::error::RecvError::Closed) => {
warn!("Attitude broadcast channel closed; broadcast loop exiting");
return;
}
Err(broadcast::error::RecvError::Lagged(n)) => {
warn!(
"GIMBAL_DEVICE_ATTITUDE_STATUS broadcast lagged by {} ticks",
n
);
continue;
}
};
let failure_flags = if tick.link_recently_failed {
GimbalDeviceErrorFlags::GIMBAL_DEVICE_ERROR_FLAGS_COMMS_ERROR
} else {
GimbalDeviceErrorFlags::empty()
};
let msg = MavMessage::GIMBAL_DEVICE_ATTITUDE_STATUS(build_attitude_status_data(
&tick.attitude,
start_time,
failure_flags,
));
broadcast_to_peers(
&socket,
&peers,
&config,
&seq,
msg,
"GIMBAL_DEVICE_ATTITUDE_STATUS",
)
.await;
}
}