470 lines
15 KiB
Rust
470 lines
15 KiB
Rust
#![allow(unused_variables)]
|
|
mod body;
|
|
mod error;
|
|
mod header;
|
|
|
|
use body::*;
|
|
use error::*;
|
|
use header::*;
|
|
|
|
use super::db::*;
|
|
|
|
use std::collections::VecDeque;
|
|
|
|
const FLAG_DELIMITER: u8 = 0x7E;
|
|
const FLAG_DELIMITER_ESCAPE: u8 = 0x7D;
|
|
|
|
pub fn parse_inbound_msg(
|
|
rawdata: &mut InboundDataWrapper,
|
|
) -> std::result::Result<Message, MessageError> {
|
|
let mut msg: Message = Message::default();
|
|
|
|
if rawdata.first_byte() != FLAG_DELIMITER {
|
|
return Err(MessageError::NotOurProtocolError);
|
|
}
|
|
|
|
match msg.parse_header(rawdata) {
|
|
Ok(_) => {
|
|
msg.inbound_check(rawdata);
|
|
}
|
|
Err(e) => {
|
|
println!("error parsing header {e}");
|
|
return Err(MessageError::BasicError);
|
|
}
|
|
};
|
|
|
|
Ok(msg)
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct InboundDataWrapper {
|
|
pub data: VecDeque<u8>,
|
|
pub index: usize,
|
|
pub started: bool,
|
|
#[allow(dead_code)]
|
|
pub ended: bool,
|
|
pub escaped: Option<u8>,
|
|
}
|
|
|
|
impl InboundDataWrapper {
|
|
pub fn new(data: Vec<u8>) -> InboundDataWrapper {
|
|
InboundDataWrapper {
|
|
data: data.into(),
|
|
index: 0,
|
|
started: false,
|
|
ended: false,
|
|
escaped: None,
|
|
}
|
|
}
|
|
|
|
fn first_byte(&self) -> u8 {
|
|
self.data[0]
|
|
}
|
|
}
|
|
|
|
impl Iterator for InboundDataWrapper {
|
|
type Item = u8;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let mut res: Option<u8> = None;
|
|
|
|
match self.escaped {
|
|
Some(o) => return Some(o),
|
|
None => {}
|
|
}
|
|
|
|
loop {
|
|
match self.data.pop_front() {
|
|
Some(o) => {
|
|
if self.started {
|
|
match o {
|
|
FLAG_DELIMITER_ESCAPE => match self.data.pop_front() {
|
|
Some(o) => match o {
|
|
0x02 => res = Some(FLAG_DELIMITER),
|
|
0x01 => res = Some(FLAG_DELIMITER_ESCAPE),
|
|
_ => {
|
|
self.escaped = Some(o);
|
|
}
|
|
},
|
|
None => res = None,
|
|
},
|
|
FLAG_DELIMITER => {
|
|
res = None;
|
|
}
|
|
_ => {
|
|
res = Some(o);
|
|
}
|
|
}
|
|
}
|
|
if o == FLAG_DELIMITER && self.index == 0 {
|
|
self.started = true;
|
|
continue;
|
|
}
|
|
}
|
|
None => {
|
|
res = None;
|
|
}
|
|
};
|
|
break;
|
|
}
|
|
self.index += 1;
|
|
|
|
res
|
|
}
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct Message {
|
|
pub header: MessageHeader,
|
|
pub content: MessageType,
|
|
pub body: Vec<u8>,
|
|
pub checksum: u8,
|
|
valid: bool,
|
|
}
|
|
|
|
impl Message {
|
|
#[allow(dead_code)]
|
|
pub fn new() -> Self {
|
|
let msg = Self::default();
|
|
return msg;
|
|
}
|
|
|
|
fn parse_header(
|
|
&mut self,
|
|
rawdata: &mut InboundDataWrapper,
|
|
) -> std::result::Result<(), NotOurProtocolError> {
|
|
let data = rawdata.into_iter();
|
|
self.header.set_id(u16::from_be_bytes(
|
|
vec![data.next().unwrap(), data.next().unwrap()]
|
|
.try_into()
|
|
.unwrap(),
|
|
));
|
|
|
|
self.header.set_properties(u16::from_be_bytes(
|
|
vec![data.next().unwrap(), data.next().unwrap()]
|
|
.try_into()
|
|
.unwrap(),
|
|
));
|
|
self.header.parse_properties();
|
|
|
|
let mut rtid: [u8; 6] = [0; 6];
|
|
for i in 0..rtid.len() {
|
|
rtid[i] = data.next().unwrap();
|
|
}
|
|
self.header.set_raw_terminal_id(rtid);
|
|
|
|
self.header.parse_terminal_id();
|
|
|
|
self.header.serial_number = u16::from_be_bytes(
|
|
vec![data.next().unwrap(), data.next().unwrap()]
|
|
.try_into()
|
|
.unwrap(),
|
|
);
|
|
|
|
for _ in 0..self.header.bodylength as usize {
|
|
self.body.push(data.next().unwrap());
|
|
}
|
|
|
|
self.checksum = data.next().unwrap();
|
|
|
|
self.parse_body();
|
|
Ok(())
|
|
}
|
|
|
|
pub fn inbound_check(&mut self, rawdata: &mut InboundDataWrapper) {
|
|
let data = rawdata.into_iter();
|
|
let mut check: u8 = 0;
|
|
|
|
let _ = data.map(|b| check ^= b);
|
|
|
|
self.inbound_validate(check);
|
|
}
|
|
|
|
fn inbound_validate(&mut self, check: u8) {
|
|
self.valid = check == 0;
|
|
}
|
|
|
|
fn _is_valid(&self) -> bool {
|
|
self.valid
|
|
}
|
|
|
|
fn parse_body(&mut self) {
|
|
match self.header.get_id() {
|
|
TerminalUniversalResponse::ID => {
|
|
let obj = TerminalUniversalResponse::new(&self.body);
|
|
self.content = MessageType::TerminalUniversalResponse(obj)
|
|
}
|
|
PlatformUniversalResponse::ID => {
|
|
let obj = PlatformUniversalResponse::new(&self.body);
|
|
self.content = MessageType::PlatformUniversalResponse(obj)
|
|
}
|
|
TerminalHeartbeat::ID => {
|
|
let obj = TerminalHeartbeat::new(&self.body);
|
|
self.content = MessageType::TerminalHeartbeat(obj)
|
|
}
|
|
TerminalRegistration::ID => {
|
|
let obj = TerminalRegistration::new(&self.body);
|
|
self.content = MessageType::TerminalRegistration(obj)
|
|
}
|
|
TerminalRegistrationReply::ID => {
|
|
let obj = TerminalRegistrationReply::new(&self.body);
|
|
self.content = MessageType::TerminalRegistrationReply(obj)
|
|
}
|
|
TerminalLogout::ID => {
|
|
let obj = TerminalLogout::new(&self.body);
|
|
self.content = MessageType::TerminalLogout(obj)
|
|
}
|
|
TerminalAuthentication::ID => {
|
|
let obj = TerminalAuthentication::new(&self.body);
|
|
self.content = MessageType::TerminalAuthentication(obj)
|
|
}
|
|
TerminalParameterSetting::ID => {
|
|
let obj = TerminalParameterSetting::new(&self.body);
|
|
self.content = MessageType::TerminalParameterSetting(obj)
|
|
}
|
|
QueryTerminalParameter::ID => {
|
|
let obj = QueryTerminalParameter::new(&self.body);
|
|
self.content = MessageType::QueryTerminalParameter(obj)
|
|
}
|
|
QueryTerminalParameterResponse::ID => {
|
|
let obj = QueryTerminalParameterResponse::new(&self.body);
|
|
self.content = MessageType::QueryTerminalParameterResponse(obj)
|
|
}
|
|
TerminalControl::ID => {
|
|
let obj = TerminalControl::new(&self.body);
|
|
self.content = MessageType::TerminalControl(obj)
|
|
}
|
|
LocationInformationReport::ID => {
|
|
let obj = LocationInformationReport::new(&self.body);
|
|
self.content = MessageType::LocationInformationReport(obj)
|
|
}
|
|
StartOfTrip::ID => {
|
|
let obj = StartOfTrip::new(&self.body);
|
|
self.content = MessageType::StartOfTrip(obj)
|
|
}
|
|
EndOfTrip::ID => {
|
|
let obj = EndOfTrip::new(&self.body);
|
|
self.content = MessageType::EndOfTrip(obj)
|
|
}
|
|
_ => {
|
|
let obj = TerminalUniversalResponse::new(&self.body);
|
|
self.content = MessageType::TerminalUniversalResponse(obj)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn send_reply(inmsg: Option<Message>) -> Option<Message> {
|
|
let mut reply: Message = Message::default();
|
|
match inmsg {
|
|
Some(inmsg) => {
|
|
let terminal_id = inmsg.header.get_raw_terminal_id().clone();
|
|
match inmsg.content {
|
|
MessageType::TerminalRegistration(t) => {
|
|
let content =
|
|
t.generate_reply(terminal_id.into(), inmsg.header.serial_number);
|
|
reply.header.build(
|
|
TerminalRegistrationReply::ID,
|
|
content.to_raw().len(),
|
|
terminal_id,
|
|
);
|
|
reply.content = MessageType::TerminalRegistrationReply(content);
|
|
}
|
|
MessageType::TerminalAuthentication(t) => {
|
|
let content = t
|
|
.generate_reply(TerminalAuthentication::ID, inmsg.header.serial_number);
|
|
reply.header.build(
|
|
PlatformUniversalResponse::ID,
|
|
content.to_raw().len(),
|
|
terminal_id,
|
|
);
|
|
reply.content = MessageType::PlatformUniversalResponse(content);
|
|
}
|
|
MessageType::LocationInformationReport(t) => {
|
|
let content = t.generate_reply(
|
|
LocationInformationReport::ID,
|
|
inmsg.header.serial_number,
|
|
);
|
|
reply.header.build(
|
|
PlatformUniversalResponse::ID,
|
|
content.to_raw().len(),
|
|
terminal_id,
|
|
);
|
|
reply.content = MessageType::PlatformUniversalResponse(content);
|
|
}
|
|
MessageType::TerminalHeartbeat(t) => {
|
|
let content =
|
|
t.generate_reply(TerminalHeartbeat::ID, inmsg.header.serial_number);
|
|
reply.header.build(
|
|
PlatformUniversalResponse::ID,
|
|
content.to_raw().len(),
|
|
terminal_id,
|
|
);
|
|
reply.content = MessageType::PlatformUniversalResponse(content);
|
|
}
|
|
MessageType::TerminalLogout(t) => {
|
|
let content =
|
|
t.generate_reply(TerminalHeartbeat::ID, inmsg.header.serial_number);
|
|
reply.header.build(
|
|
PlatformUniversalResponse::ID,
|
|
content.to_raw().len(),
|
|
terminal_id,
|
|
);
|
|
reply.content = MessageType::PlatformUniversalResponse(content);
|
|
}
|
|
_ => {
|
|
println!("no type");
|
|
return None;
|
|
}
|
|
}
|
|
}
|
|
None => {}
|
|
}
|
|
reply.outbound_finalize();
|
|
Some(reply)
|
|
}
|
|
|
|
pub fn store(inmsg: &Message) -> Option<DbLog> {
|
|
let res = match inmsg.content {
|
|
MessageType::LocationInformationReport(ref t) => Some(DbLog {
|
|
serial: inmsg.header.serial_number,
|
|
time: t.time.format("%Y-%m-%d %H:%M:%S").to_string(),
|
|
latitude: t.latitude,
|
|
longitude: t.longitude,
|
|
speed: t.speed,
|
|
height: t.height,
|
|
direction: t.direction,
|
|
is_satellite: t.is_satellite(),
|
|
}),
|
|
_ => None,
|
|
};
|
|
res
|
|
}
|
|
|
|
pub fn outbound_finalize(&mut self) {
|
|
let mut checksum: u8 = 0;
|
|
|
|
for v in vec![self.header.to_raw(), self.content.to_raw()] {
|
|
for b in v {
|
|
checksum = checksum ^ b;
|
|
}
|
|
}
|
|
|
|
self.checksum = checksum;
|
|
self.valid = true;
|
|
}
|
|
|
|
pub fn to_raw(&self) -> VecDeque<u8> {
|
|
let mut resp: VecDeque<u8> = VecDeque::new();
|
|
|
|
for b in self.header.to_raw() {
|
|
for i in Self::escape_outbound(b) {
|
|
resp.push_back(i);
|
|
}
|
|
}
|
|
|
|
for b in self.content.to_raw() {
|
|
for i in Self::escape_outbound(b) {
|
|
resp.push_back(i);
|
|
}
|
|
}
|
|
|
|
resp.push_back(self.checksum);
|
|
|
|
resp.push_front(FLAG_DELIMITER);
|
|
resp.push_back(FLAG_DELIMITER);
|
|
resp
|
|
}
|
|
|
|
fn escape_outbound(in_data: u8) -> Vec<u8> {
|
|
let mut res: Vec<u8> = Vec::new();
|
|
match in_data {
|
|
FLAG_DELIMITER => res = vec![FLAG_DELIMITER_ESCAPE, 0x02],
|
|
FLAG_DELIMITER_ESCAPE => res = vec![FLAG_DELIMITER_ESCAPE, 0x01],
|
|
_ => {
|
|
res.push(in_data);
|
|
}
|
|
}
|
|
res
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for Message {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
write!(
|
|
f,
|
|
"Message: (header: {}), (content: {}), checksum: {}, valid: {}",
|
|
self.header, self.content, self.checksum, self.valid
|
|
)
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
#[derive(Debug, Clone)]
|
|
pub enum MessageType {
|
|
None,
|
|
TerminalUniversalResponse(TerminalUniversalResponse),
|
|
PlatformUniversalResponse(PlatformUniversalResponse),
|
|
TerminalHeartbeat(TerminalHeartbeat),
|
|
TerminalRegistration(TerminalRegistration),
|
|
TerminalRegistrationReply(TerminalRegistrationReply),
|
|
TerminalLogout(TerminalLogout),
|
|
TerminalAuthentication(TerminalAuthentication),
|
|
TerminalParameterSetting(TerminalParameterSetting),
|
|
QueryTerminalParameter(QueryTerminalParameter),
|
|
QueryTerminalParameterResponse(QueryTerminalParameterResponse),
|
|
TerminalControl(TerminalControl),
|
|
LocationInformationReport(LocationInformationReport),
|
|
StartOfTrip(StartOfTrip),
|
|
EndOfTrip(EndOfTrip),
|
|
}
|
|
|
|
impl Default for MessageType {
|
|
fn default() -> Self {
|
|
Self::None
|
|
}
|
|
}
|
|
|
|
impl BodyMessage for MessageType {
|
|
fn to_raw(&self) -> Vec<u8> {
|
|
let res = match self {
|
|
MessageType::TerminalRegistrationReply(o) => o.to_raw(),
|
|
MessageType::PlatformUniversalResponse(o) => o.to_raw(),
|
|
//MessageType::TerminalHeartbeat(o) => o.to_raw(),
|
|
_ => vec![],
|
|
};
|
|
res
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for MessageType {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
let res = match self {
|
|
MessageType::TerminalRegistration(o) => o.fmt(f),
|
|
MessageType::LocationInformationReport(o) => o.fmt(f),
|
|
MessageType::TerminalHeartbeat(o) => o.fmt(f),
|
|
MessageType::TerminalAuthentication(o) => o.fmt(f),
|
|
_ => write!(f, "{:?}", self),
|
|
};
|
|
res
|
|
}
|
|
}
|
|
|
|
/*
|
|
{
|
|
use std::fs::OpenOptions;
|
|
use std::io::prelude::*;
|
|
|
|
let mut file = OpenOptions::new()
|
|
.write(true)
|
|
.append(true)
|
|
.open("data/log.txt")
|
|
.unwrap();
|
|
|
|
//if let Err(e) = writeln!(file, ) {
|
|
// eprintln!("Couldn't write to file: {}", e);
|
|
//}
|
|
file.write(format!("{},{},{}\n", t.time, t.latitude, t.longitude).as_bytes())
|
|
.unwrap();
|
|
}
|
|
*/
|