update micodus_server
This commit is contained in:
parent
99b779c5f7
commit
c52556eb2e
30
Cargo.lock
generated
30
Cargo.lock
generated
@ -38,6 +38,15 @@ dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bcd-convert"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71a18a98ca044380fc93aa29f45577996d8aee9b689176b56327f3080280d798"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
@ -88,6 +97,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
name = "micodus_server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bcd-convert",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@ -224,6 +234,26 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.42.0"
|
||||
|
@ -4,4 +4,5 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bcd-convert = "0.1.0"
|
||||
tokio = { version = "1.42", features = ["full", "sync"] }
|
||||
|
25
README.md
Normal file
25
README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# micodus_server
|
||||
|
||||
Micodus platform server
|
||||
|
||||
## Build
|
||||
|
||||
```cargo b -r```
|
||||
|
||||
## License
|
||||
|
||||
```
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
```
|
14
src/main.rs
14
src/main.rs
@ -31,12 +31,12 @@ pub async fn apiserver() -> io::Result<()> {
|
||||
loop {
|
||||
match listener.accept().await {
|
||||
Ok((socket, _remote_addr)) => {
|
||||
let mut buf = vec![0; 256];
|
||||
let mut buf = vec![0; 1024];
|
||||
|
||||
match socket.readable().await {
|
||||
Ok(_) => {
|
||||
match socket.try_read(&mut buf) {
|
||||
Ok(_) => {}
|
||||
Ok(_) => handle(&buf),
|
||||
Err(e) => {
|
||||
println!("error: {e}");
|
||||
continue;
|
||||
@ -48,10 +48,6 @@ pub async fn apiserver() -> io::Result<()> {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut rawdata = buf.to_vec();
|
||||
let msg = parse_inbound_msg(&mut rawdata);
|
||||
println!("{:X?}", msg);
|
||||
}
|
||||
Err(err) => {
|
||||
println!("error: {err}");
|
||||
@ -61,3 +57,9 @@ pub async fn apiserver() -> io::Result<()> {
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn handle(buf: &Vec<u8>) {
|
||||
let mut rawdata = buf.to_vec();
|
||||
let msg = parse_inbound_msg(&mut rawdata);
|
||||
println!("{}", msg);
|
||||
}
|
||||
|
@ -1,3 +1,26 @@
|
||||
pub trait BodyMessage {
|
||||
fn build() {}
|
||||
fn print(&mut self, body: &Vec<u8>) {
|
||||
println!("{:?}", body);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! generate_impl {
|
||||
($($t:ty),*) => {
|
||||
$(
|
||||
impl BodyMessage for $t {
|
||||
fn build() {}
|
||||
}
|
||||
impl $t {
|
||||
pub fn new(body: &Vec<u8>) -> Self {
|
||||
let mut res = Self::default();
|
||||
res.parse(body);
|
||||
res
|
||||
}
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TerminalUniversalResponse {
|
||||
pub answer_serial_no: u16,
|
||||
@ -5,7 +28,11 @@ pub struct TerminalUniversalResponse {
|
||||
pub result: u8,
|
||||
}
|
||||
|
||||
impl BodyMessage for TerminalUniversalResponse {}
|
||||
impl TerminalUniversalResponse {
|
||||
const ID: u16 = 0x100;
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
pub fn debug() {}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct PlatformUniversalResponse {
|
||||
@ -14,9 +41,17 @@ pub struct PlatformUniversalResponse {
|
||||
pub result: u8,
|
||||
}
|
||||
|
||||
impl PlatformUniversalResponse {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TerminalHeartbeat {}
|
||||
|
||||
impl TerminalHeartbeat {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TerminalRegistration {
|
||||
pub provincial_id: u16,
|
||||
@ -28,6 +63,24 @@ pub struct TerminalRegistration {
|
||||
pub license_plate: String,
|
||||
}
|
||||
|
||||
impl TerminalRegistration {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
pub fn generate_reply(&self, serial: u16) -> TerminalRegistrationReply {
|
||||
let mut res = TerminalRegistrationReply::default();
|
||||
res.answer_serial_no = serial;
|
||||
res.result = TerminalRegistrationResult::Success as u8;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
enum TerminalRegistrationResult {
|
||||
Success = 0x00,
|
||||
VehicleRegistered,
|
||||
VehicleNotInDatabase,
|
||||
TerminalRegistered,
|
||||
VehicleNotInDatabase2,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TerminalRegistrationReply {
|
||||
pub answer_serial_no: u16,
|
||||
@ -35,20 +88,47 @@ pub struct TerminalRegistrationReply {
|
||||
pub authentication_code: String,
|
||||
}
|
||||
|
||||
impl TerminalRegistrationReply {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
pub fn body_to_vec(&self) -> Vec<u8> {
|
||||
let mut res: Vec<u8> = vec![];
|
||||
for b in self.answer_serial_no.to_be_bytes() {
|
||||
res.push(b);
|
||||
}
|
||||
res.push(self.result);
|
||||
for b in self.authentication_code.as_bytes() {
|
||||
res.push(*b);
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TerminalLogout {}
|
||||
|
||||
impl TerminalLogout {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TerminalAuthentication {
|
||||
pub authentication_code: String,
|
||||
}
|
||||
|
||||
impl TerminalAuthentication {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TerminalParameterSetting {
|
||||
pub total_parameters: u8,
|
||||
pub parameters: Vec<TerminalParameterData>,
|
||||
}
|
||||
|
||||
impl TerminalParameterSetting {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TerminalParameterData {
|
||||
pub parameter_id: u32,
|
||||
@ -56,8 +136,15 @@ pub struct TerminalParameterData {
|
||||
pub parameter_value: u8,
|
||||
}
|
||||
|
||||
impl TerminalParameterData {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct QueryTerminalParameter {}
|
||||
impl QueryTerminalParameter {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct QueryTerminalParameterResponse {
|
||||
@ -66,12 +153,20 @@ pub struct QueryTerminalParameterResponse {
|
||||
pub parameters: Vec<TerminalParameterData>,
|
||||
}
|
||||
|
||||
impl QueryTerminalParameterResponse {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TerminalControl {
|
||||
pub command_word: u8,
|
||||
pub command_parameter: String,
|
||||
}
|
||||
|
||||
impl TerminalControl {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct LocationInformationReport {
|
||||
pub alert_mark: u32,
|
||||
@ -84,30 +179,36 @@ pub struct LocationInformationReport {
|
||||
pub time: u32,
|
||||
}
|
||||
|
||||
impl LocationInformationReport {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct StartOfTrip {}
|
||||
impl StartOfTrip {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct EndOfTrip {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BodyType {
|
||||
TerminalUniversalResponse = 0x0001,
|
||||
PlatformUniversalResponse = 0x8001,
|
||||
TerminalHeartbeat = 0x0002,
|
||||
TerminalRegistration = 0x0100,
|
||||
TerminalRegistrationReply = 0x8100,
|
||||
TerminalLogout = 0x0003,
|
||||
TerminalAuthentication = 0x0102,
|
||||
TerminalParameterSetting = 0x8103,
|
||||
QueryTerminalParameter = 0x8104,
|
||||
QueryTerminalParameterResponse = 0x0104,
|
||||
TerminalControl = 0x8105,
|
||||
LocationInformationReport = 0x0200,
|
||||
StartOfTrip = 0x0202,
|
||||
EndOfTrip = 0x0203,
|
||||
impl EndOfTrip {
|
||||
pub fn parse(&mut self, body: &Vec<u8>) {}
|
||||
}
|
||||
|
||||
pub trait BodyMessage {
|
||||
fn parse() {}
|
||||
}
|
||||
generate_impl!(
|
||||
TerminalUniversalResponse,
|
||||
PlatformUniversalResponse,
|
||||
TerminalHeartbeat,
|
||||
TerminalRegistration,
|
||||
TerminalRegistrationReply,
|
||||
TerminalLogout,
|
||||
TerminalAuthentication,
|
||||
TerminalParameterSetting,
|
||||
TerminalParameterData,
|
||||
QueryTerminalParameter,
|
||||
QueryTerminalParameterResponse,
|
||||
TerminalControl,
|
||||
LocationInformationReport,
|
||||
StartOfTrip,
|
||||
EndOfTrip
|
||||
);
|
||||
|
@ -1,14 +1,20 @@
|
||||
mod body;
|
||||
|
||||
use body::*;
|
||||
use std::fmt::*;
|
||||
|
||||
use bcd_convert::BcdNumber;
|
||||
|
||||
const FLAGDELIMITER: u8 = 0x7E;
|
||||
|
||||
#[allow(dead_code)]
|
||||
const PropsSizeMask: u16 = 0x01FF;
|
||||
const PropsSubcontractMask: u16 = 0x3000;
|
||||
const PropsReservedMask: u16 = 0xC000;
|
||||
const PropsDataEncryptionMask: u16 = 0x0E00;
|
||||
const PROPS_SIZE_MASK: u16 = 0x01FF;
|
||||
#[allow(dead_code)]
|
||||
const PROPS_SUBCONTRACT_MASK: u16 = 0x3000;
|
||||
#[allow(dead_code)]
|
||||
const PROPS_RESERVED_MASK: u16 = 0xC000;
|
||||
#[allow(dead_code)]
|
||||
const PROPS_DATAENCRYPTION_MASK: u16 = 0x0E00;
|
||||
|
||||
pub fn parse_inbound_msg(rawdata: &mut Vec<u8>) -> Message {
|
||||
let mut msg: Message = Message::default();
|
||||
@ -22,11 +28,22 @@ pub fn parse_inbound_msg(rawdata: &mut Vec<u8>) -> Message {
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Message {
|
||||
pub header: MessageHeader,
|
||||
pub content: MessageType,
|
||||
pub body: Vec<u8>,
|
||||
pub checksum: u8,
|
||||
valid: bool,
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Message {
|
||||
pub fn parse_header(&mut self, rawdata: &mut Vec<u8>) {
|
||||
let mut data = rawdata.clone().into_iter();
|
||||
@ -34,17 +51,24 @@ impl Message {
|
||||
if first_byte == FLAGDELIMITER {
|
||||
println!("first flag ok");
|
||||
};
|
||||
self.header.id = ((data.next().unwrap() as u16) << 8) + data.next().unwrap() as u16;
|
||||
let mut tmpid = vec![];
|
||||
tmpid.push(data.next().unwrap());
|
||||
tmpid.push(data.next().unwrap());
|
||||
self.header.id = u16::from_be_bytes(tmpid.try_into().unwrap());
|
||||
//self.header.id = ((data.next().unwrap() as u16) << 8) + data.next().unwrap() as u16;
|
||||
|
||||
self.header.properties = ((data.next().unwrap() as u16) << 8) + data.next().unwrap() as u16;
|
||||
for i in 0..self.header.terminal_id.len() {
|
||||
self.header.terminal_id[i] = data.next().unwrap();
|
||||
}
|
||||
self.parse_properties();
|
||||
|
||||
for i in 0..self.header.raw_terminal_id.len() {
|
||||
self.header.raw_terminal_id[i] = data.next().unwrap();
|
||||
}
|
||||
self.parse_terminal_id();
|
||||
|
||||
self.header.serial_number =
|
||||
((data.next().unwrap() as u16) << 8) + data.next().unwrap() as u16;
|
||||
|
||||
for _ in 0..self.header.properties as usize {
|
||||
for _ in 0..self.header.bodylength as usize {
|
||||
self.body.push(data.next().unwrap());
|
||||
}
|
||||
|
||||
@ -55,10 +79,16 @@ impl Message {
|
||||
} else {
|
||||
println!("error with endflag");
|
||||
}
|
||||
self.parse_body();
|
||||
}
|
||||
|
||||
pub fn parse_properties(&mut self) {
|
||||
self.header.bodylength = (self.header.properties & PropsSizeMask) as u8;
|
||||
self.header.bodylength = (self.header.properties & PROPS_SIZE_MASK) as u8;
|
||||
}
|
||||
|
||||
pub fn parse_terminal_id(&mut self) {
|
||||
let code = BcdNumber::try_from(&self.header.raw_terminal_id as &[u8]).unwrap();
|
||||
self.header.terminal_id = code.to_u64().unwrap();
|
||||
}
|
||||
|
||||
pub fn check(&mut self, rawdata: &mut Vec<u8>) {
|
||||
@ -78,21 +108,128 @@ impl Message {
|
||||
}
|
||||
|
||||
fn validate(&mut self, check: u8) {
|
||||
println!("{}", BodyType::TerminalHeartbeat as u16);
|
||||
self.valid = check == 0;
|
||||
}
|
||||
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.valid
|
||||
}
|
||||
|
||||
pub fn parse_body(&mut self) {
|
||||
match self.header.id {
|
||||
0x0001 => {
|
||||
self.content = MessageType::TerminalUniversalResponse(
|
||||
TerminalUniversalResponse::new(&self.body),
|
||||
)
|
||||
}
|
||||
0x8001 => {
|
||||
self.content = MessageType::PlatformUniversalResponse(
|
||||
PlatformUniversalResponse::new(&self.body),
|
||||
)
|
||||
}
|
||||
0x0002 => {
|
||||
self.content = MessageType::TerminalHeartbeat(TerminalHeartbeat::new(&self.body))
|
||||
}
|
||||
0x0100 => {
|
||||
self.content =
|
||||
MessageType::TerminalRegistration(TerminalRegistration::new(&self.body))
|
||||
}
|
||||
0x8100 => {
|
||||
self.content = MessageType::TerminalRegistrationReply(
|
||||
TerminalRegistrationReply::new(&self.body),
|
||||
)
|
||||
}
|
||||
0x0003 => self.content = MessageType::TerminalLogout(TerminalLogout::new(&self.body)),
|
||||
0x0102 => {
|
||||
self.content =
|
||||
MessageType::TerminalAuthentication(TerminalAuthentication::new(&self.body))
|
||||
}
|
||||
0x8103 => {
|
||||
self.content =
|
||||
MessageType::TerminalParameterSetting(TerminalParameterSetting::new(&self.body))
|
||||
}
|
||||
0x8104 => {
|
||||
self.content =
|
||||
MessageType::QueryTerminalParameter(QueryTerminalParameter::new(&self.body))
|
||||
}
|
||||
0x0104 => {
|
||||
self.content = MessageType::QueryTerminalParameterResponse(
|
||||
QueryTerminalParameterResponse::new(&self.body),
|
||||
)
|
||||
}
|
||||
0x8105 => self.content = MessageType::TerminalControl(TerminalControl::new(&self.body)),
|
||||
0x0200 => {
|
||||
self.content = MessageType::LocationInformationReport(
|
||||
LocationInformationReport::new(&self.body),
|
||||
)
|
||||
}
|
||||
0x0202 => self.content = MessageType::StartOfTrip(StartOfTrip::new(&self.body)),
|
||||
0x0203 => self.content = MessageType::EndOfTrip(EndOfTrip::new(&self.body)),
|
||||
_ => self.content = MessageType::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_reply(msg: Message) -> Message {
|
||||
let mut reply = Message::default();
|
||||
match msg.content {
|
||||
MessageType::TerminalRegistration(t) => {
|
||||
reply.content = MessageType::TerminalRegistrationReply(
|
||||
t.generate_reply(msg.header.serial_number),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
reply
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct MessageHeader {
|
||||
pub id: u16,
|
||||
id: u16,
|
||||
properties: u16,
|
||||
pub bodylength: u8,
|
||||
pub encryption: bool,
|
||||
pub terminal_id: [u8; 6],
|
||||
raw_terminal_id: [u8; 6],
|
||||
pub terminal_id: u64,
|
||||
pub serial_number: u16,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MessageHeader {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"message header: id: {:X?}, length: {}, terminal id: {}, serial: {}",
|
||||
self.id, self.bodylength, self.terminal_id, self.serial_number
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
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 std::fmt::Display for MessageType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user