use crate::proto::CotEvent;
use std::time::{SystemTime, UNIX_EPOCH};
fn ms_to_tak_time(ms: u64) -> String {
let secs = ms / 1000;
let dt = chrono::DateTime::from_timestamp(secs as i64, 0)
.unwrap_or_else(|| chrono::Utc::now());
dt.format("%Y-%m-%dT%H:%M:%S.%3fZ").to_string()
}
pub fn encode_cot_event_xml(event: &CotEvent) -> String {
let send_time = ms_to_tak_time(event.send_time);
let start_time = ms_to_tak_time(event.start_time);
let stale_time = ms_to_tak_time(event.stale_time);
let mut xml = format!(
r#"<?xml version="1.0" encoding="UTF-8"?>
<event version="2.0" uid="{}" type="{}" time="{}" start="{}" stale="{}" how="{}">"#,
event.uid, event.r#type, send_time, start_time, stale_time, event.how
);
xml.push_str(&format!(
r#"
<point lat="{}" lon="{}" hae="{}" ce="{}" le="{}"/>"#,
event.lat, event.lon, event.hae, event.ce, event.le
));
if let Some(ref detail) = event.detail {
xml.push_str("\n <detail>");
if let Some(ref contact) = detail.contact {
xml.push_str(&format!(
r#"
<contact callsign="{}" endpoint="{}"/>"#,
contact.callsign, contact.endpoint
));
}
if let Some(ref group) = detail.group {
xml.push_str(&format!(
r#"
<__group name="{}" role="{}"/>"#,
group.name, group.role
));
}
if let Some(ref takv) = detail.takv {
xml.push_str(&format!(
r#"
<takv version="{}" platform="{}" os="{}" device="{}"/>"#,
takv.version, takv.platform, takv.os, takv.device
));
}
if !detail.xml_detail.is_empty() {
xml.push_str("\n ");
xml.push_str(&detail.xml_detail);
}
xml.push_str("\n </detail>");
}
xml.push_str("\n</event>");
xml
}
pub fn create_protocol_request(version: u32) -> String {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
let time_str = ms_to_tak_time(now);
format!(
r#"<?xml version="1.0" encoding="UTF-8"?>
<event version="2.0" uid="protouid" type="t-x-takp-q" time="{}" start="{}" stale="{}" how="m-g">
<point lat="0.0" lon="0.0" hae="0.0" ce="999999" le="999999"/>
<detail>
<TakControl>
<TakRequest version="{}"/>
</TakControl>
</detail>
</event>"#,
time_str, time_str, time_str, version
)
}
pub fn is_protocol_support(xml: &str) -> bool {
xml.contains("t-x-takp-v") && xml.contains("<TakProtocolSupport")
}
pub fn is_protocol_response(xml: &str) -> bool {
xml.contains("t-x-takp-r") && xml.contains("<TakResponse")
}
pub fn is_protocol_response_success(xml: &str) -> bool {
xml.contains(r#"status="true""#)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_basic_event() {
let event = CotEvent {
r#type: "a-f-G-U-C".to_string(),
uid: "TEST-1".to_string(),
send_time: 1234567890000,
start_time: 1234567890000,
stale_time: 1234567950000,
how: "m-g".to_string(),
lat: 37.7749,
lon: -122.4194,
hae: 10.0,
ce: 9.9,
le: 9.9,
..Default::default()
};
let xml = encode_cot_event_xml(&event);
assert!(xml.contains(r#"uid="TEST-1""#));
assert!(xml.contains(r#"type="a-f-G-U-C""#));
assert!(xml.contains(r#"lat="37.7749""#));
}
#[test]
fn test_protocol_request() {
let xml = create_protocol_request(1);
assert!(xml.contains(r#"type="t-x-takp-q""#));
assert!(xml.contains(r#"<TakRequest version="1"/>"#));
}
}