1use std::net::{IpAddr, Ipv4Addr};
4use std::sync::Arc;
5use std::time::SystemTime;
6
7use bytes::Bytes;
8use tl_gige::gvcp::consts as gvcp_consts;
9use tl_gige::message::{EventPacket, EventSocket};
10use tracing::{debug, info, warn};
11
12use crate::time::TimeSync;
13use crate::GenicamError;
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Event {
18 pub id: u16,
20 pub ts_dev: u64,
22 pub ts_host: SystemTime,
24 pub data: Bytes,
26}
27
28pub 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 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 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; 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
80pub(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 transport
91 .write(gvcp_consts::MESSAGE_DESTINATION_PORT, &port.to_be_bytes())
92 .map_err(|err| GenicamError::transport(format!("write message port: {err}")))?;
93 info!(%ip, port, "configured message channel via raw registers");
94 Ok(())
95}
96
97pub(crate) fn enable_event_raw<T: crate::genapi::RegisterIo>(
99 transport: &T,
100 event_id: u16,
101 on: bool,
102) -> Result<(), GenicamError> {
103 let index = (event_id / 32) as u64;
104 let bit = 1u32 << (event_id % 32);
105 let addr =
106 gvcp_consts::EVENT_NOTIFICATION_BASE + index * gvcp_consts::EVENT_NOTIFICATION_STRIDE;
107 let current = transport
108 .read(addr, 4)
109 .map_err(|err| GenicamError::transport(format!("read event mask: {err}")))?;
110 if current.len() != 4 {
111 return Err(GenicamError::transport("event mask length mismatch"));
112 }
113 let mut bytes = [0u8; 4];
114 bytes.copy_from_slice(¤t);
115 let mut value = u32::from_be_bytes(bytes);
116 if on {
117 value |= bit;
118 } else {
119 value &= !bit;
120 }
121 transport
122 .write(addr, &value.to_be_bytes())
123 .map_err(|err| GenicamError::transport(format!("write event mask: {err}")))?;
124 info!(event_id, enabled = on, "updated event notification mask");
125 Ok(())
126}
127
128pub(crate) fn parse_event_id(text: &str) -> Option<u16> {
130 if let Some(stripped) = text.strip_prefix("0x") {
131 u16::from_str_radix(stripped, 16).ok()
132 } else if let Some(stripped) = text.strip_prefix("0X") {
133 u16::from_str_radix(stripped, 16).ok()
134 } else {
135 text.parse::<u16>().ok()
136 }
137}
138
139pub(crate) async fn bind_socket(ip: IpAddr, port: u16) -> Result<EventSocket, GenicamError> {
141 EventSocket::bind(ip, port)
142 .await
143 .map_err(|err| GenicamError::transport(format!("bind event socket: {err}")))
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use std::net::SocketAddr;
150
151 #[test]
152 fn parse_numeric_event_ids() {
153 assert_eq!(parse_event_id("1234"), Some(1234));
154 assert_eq!(parse_event_id("0x00AF"), Some(0x00AF));
155 assert_eq!(parse_event_id("0XFF10"), Some(0xFF10));
156 assert_eq!(parse_event_id("not-a-number"), None);
157 }
158
159 #[test]
160 fn map_packet_without_sync() {
161 let packet = EventPacket {
162 src: SocketAddr::from(([127, 0, 0, 1], 4000)),
163 event_id: 0x1000,
164 timestamp_dev: 42,
165 stream_channel: 0,
166 block_id: 0,
167 payload: Bytes::from_static(b"abcd"),
168 };
169 let event = EventStream::map_packet(packet.clone(), None);
170 assert_eq!(event.id, packet.event_id);
171 assert_eq!(event.ts_dev, packet.timestamp_dev);
172 assert_eq!(event.data, packet.payload);
173 }
174}