Skip to main content

viva_genicam/
events.rs

1//! High-level helpers for the GVCP message/event channel.
2
3use std::net::{IpAddr, Ipv4Addr};
4use std::sync::Arc;
5use std::time::SystemTime;
6
7use bytes::Bytes;
8use tracing::{debug, info, warn};
9use viva_gige::gvcp::consts as gvcp_consts;
10use viva_gige::message::{EventPacket, EventSocket};
11
12use crate::GenicamError;
13use crate::time::TimeSync;
14
15/// Public representation of a GigE Vision event.
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Event {
18    /// Raw event identifier reported by the device.
19    pub id: u16,
20    /// Device timestamp associated with the event (ticks).
21    pub ts_dev: u64,
22    /// Host timestamp mapped from the device ticks when synchronisation is available.
23    pub ts_host: SystemTime,
24    /// Raw payload bytes following the event header.
25    pub data: Bytes,
26}
27
28/// Asynchronous stream of events delivered over the GVCP message channel.
29pub struct EventStream {
30    socket: EventSocket,
31    time_sync: Option<Arc<TimeSync>>,
32}
33
34impl EventStream {
35    pub(crate) fn new(socket: EventSocket, time_sync: Option<Arc<TimeSync>>) -> Self {
36        Self { socket, time_sync }
37    }
38
39    /// Receive the next event emitted by the device.
40    pub async fn next(&self) -> Result<Event, GenicamError> {
41        let packet = self
42            .socket
43            .recv()
44            .await
45            .map_err(|err| GenicamError::transport(format!("gvcp message recv: {err}")))?;
46        debug!(
47            event_id = packet.event_id,
48            ts_dev = packet.timestamp_dev,
49            "event received"
50        );
51        Ok(Self::map_packet(packet, self.time_sync.clone()))
52    }
53
54    /// Access the local socket address used by the stream.
55    pub fn local_addr(&self) -> Result<std::net::SocketAddr, GenicamError> {
56        self.socket
57            .local_addr()
58            .map_err(|err| GenicamError::transport(format!("gvcp local addr: {err}")))
59    }
60
61    fn map_packet(packet: EventPacket, sync: Option<Arc<TimeSync>>) -> Event {
62        let ts_host = match sync {
63            Some(sync) if sync.len() > 1 => sync.to_host_time(packet.timestamp_dev),
64            Some(sync) => {
65                warn!("insufficient time sync samples; using current system time");
66                let _ = sync; // keep `sync` alive for future samples
67                SystemTime::now()
68            }
69            None => SystemTime::now(),
70        };
71        Event {
72            id: packet.event_id,
73            ts_dev: packet.timestamp_dev,
74            ts_host,
75            data: packet.payload,
76        }
77    }
78}
79
80/// Attempt to configure the GVCP message channel directly when SFNC nodes are missing.
81pub(crate) fn configure_message_channel_raw<T: crate::genapi::RegisterIo>(
82    transport: &T,
83    ip: Ipv4Addr,
84    port: u16,
85) -> Result<(), GenicamError> {
86    let addr = gvcp_consts::MESSAGE_DESTINATION_ADDRESS;
87    transport
88        .write(addr, &ip.octets())
89        .map_err(|err| GenicamError::transport(format!("write message addr: {err}")))?;
90    // GevMCP is 32-bit with the port in the low half; a bare `u16` write puts
91    // it in the high half.
92    transport
93        .write(
94            gvcp_consts::MESSAGE_DESTINATION_PORT,
95            &u32::from(port).to_be_bytes(),
96        )
97        .map_err(|err| GenicamError::transport(format!("write message port: {err}")))?;
98    info!(%ip, port, "configured message channel via raw registers");
99    Ok(())
100}
101
102// There is deliberately no raw fallback for *enabling* an event.
103//
104// One used to live here, toggling a bit in a "notification mask" at
105// 0x0900_0300 + id/32. No such bootstrap register exists: event delivery is
106// selected through the GenApi `EventSelector` / `EventNotification` features,
107// and the address was invented alongside the equally invented message-channel
108// pair next to it. Writing to it could only corrupt whatever a real device
109// keeps at that address, so a camera without those SFNC nodes now gets an
110// error naming what is missing (ADR-0019).
111
112/// Bind an [`EventSocket`] on the provided interface.
113pub(crate) async fn bind_socket(ip: IpAddr, port: u16) -> Result<EventSocket, GenicamError> {
114    EventSocket::bind(ip, port)
115        .await
116        .map_err(|err| GenicamError::transport(format!("bind event socket: {err}")))
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122    use std::net::SocketAddr;
123
124    #[test]
125    fn map_packet_without_sync() {
126        let packet = EventPacket {
127            src: SocketAddr::from(([127, 0, 0, 1], 4000)),
128            event_id: 0x1000,
129            timestamp_dev: 42,
130            stream_channel: 0,
131            block_id: 0,
132            payload: Bytes::from_static(b"abcd"),
133        };
134        let event = EventStream::map_packet(packet.clone(), None);
135        assert_eq!(event.id, packet.event_id);
136        assert_eq!(event.ts_dev, packet.timestamp_dev);
137        assert_eq!(event.data, packet.payload);
138    }
139}