1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
use std::io::Write;
use byteorder::WriteBytesExt;
use integer_encoding::VarIntWriter;
use super::{Topic, Message, SDO, Field, Data, DataType, fields::{PAGE_SIZE, TIMEOUT}};
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
pub enum Error {
#[error("io error")]
Io(#[from] std::io::Error)
}
pub fn encode_field(header: &Field, data: &Option<Data>) -> Vec<u8> {
let mut buf = vec![];
let b = 0x00;
let has_null = false;
buf.write_u8(((header.data_type as u8) << 3) + ((header.wire_type as u8) << 1) + if has_null { 1 } else { 0 });
buf.write_varint((header.field_id.unwrap_or(0) << 1) + (if header.data_type == DataType::DateTime { 1 } else { 0 }));
match &data {
Some(Data::StringW(array)) => {
for str in array.iter().flatten() {
buf.write_varint(str.len() + 1);
buf.write_u8(0);
buf.write(str.as_bytes());
}
},
Some(Data::AsciiString(array)) => {
for str in array.iter().flatten() {
buf.write_varint(str.0.len() + 1);
buf.write(str.0.as_bytes());
}
},
Some(Data::Long(array) | Data::Short(array)) => {
for value in array.iter().flatten() {
buf.write_varint(**value);
}
},
Some(Data::SDO(array)) => {
for sdo in array.iter().flatten() {
buf.write_all(&sdo.encode());
}
},
Some(Data::Bool(array)) => {
// buf.write_u8(0x28);
let mut bytes = vec![];
let mut n = 0;
let mut s = 0u8;
for (r, value) in array.into_iter().enumerate() {
if let Some(value) = value {
if n > 0 && n % 8 == 0 {
bytes.push(s);
s = 0;
n = 0;
}
s += (if true == *value { 1 } else { 0 }) << (7 - (n % 8));
n += 1;
}
}
if n > 0 {
bytes.push(s)
}
// buf.write_varint(bytes.len());
for b in bytes {
buf.write_u8(b);
}
}
type_ => warn!(?type_, "unknown type")
}
buf
}
impl SDO {
pub fn encode(&self) -> Vec<u8> {
let mut buf = vec![];
buf.write_u8(0x17);
buf.write_varint((self.topic as i32 + 1) as u32);
for (header, data) in &self.fields {
buf.write(&encode_field(header, data));
}
buf.write_u8(0);
buf
}
}
impl Message {
pub fn encode(&self) -> Vec<u8> {
let mut buf = vec![];
let mut header_sdo = SDO::new(Topic::UndefinedTopic);
header_sdo.push_string_w(74, vec![self.id.clone()]);
buf.write(&header_sdo.encode());
let mut payload = self.sdo.clone();
if self.timeout.is_some() {
payload.push_string_w(TIMEOUT, self.timeout.clone())
}
payload.push_string_w(PAGE_SIZE, Some(self.page_size.unwrap_or(1000).to_string()));
buf.write(&self.sdo.encode());
buf
}
}
#[cfg(test)]
mod test {
use std::{io::{Cursor, Write}, fs::File};
use crate::sdo::decode::read_msg;
#[test]
pub fn test_decode_encode() -> miette::Result<()> {
/*let buf: Vec<u8> = include_bytes!("../../../../data/mitm_TrLoginInfo.bin").to_vec();
let msg = read_msg(&mut Cursor::new(buf.clone()))?;
let newbuf = msg.encode();
let mut f = File::create("./mitm_TrLoginInfo.reencode.bin").unwrap();
f.write_all(&newbuf).unwrap();
assert_eq!(buf, newbuf);*/
Ok(())
}
/* #[test]
pub fn test_session_start() -> miette::Result<()> {
static WS_DUMP: &[u8] = include_bytes!("../../../../data/tx_session_start.bin");
println!("expected: {:x?}", WS_DUMP);
let mut message = Message::new(Topic::TdQuote);
message.fields.push(Field {
field_id: 77, // UserName
data: crate::sdo::encode::Data::StringW(vec![Some(Box::new("IN8001646".to_string()))])
});
message.fields.push(Field {
field_id: 486, // CompanyName
data: crate::sdo::encode::Data::StringW(vec![Some(Box::new("invast".to_string()))])
});
message.fields.push(Field {
field_id: 489, // ClientType
data: crate::sdo::encode::Data::Long(vec![Some(Box::new(ClientType::TraderPlus as u32))])
});
message.fields.push(Field {
field_id: 499,
data: crate::sdo::encode::Data::Long(vec![Some(Box::new(1))])
});
message.fields.push(Field {
field_id: 6140, // acsLoginToken
data: crate::sdo::encode::Data::StringW(vec![Some(Box::new("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".to_string()))])
});
message.fields.push(Field {
field_id: 3961, // entryId
data: crate::sdo::encode::Data::StringW(vec![Some(Box::new("invast".to_string()))])
});
message.fields.push(Field {
field_id: 9161, // entryIdToKick
data: crate::sdo::encode::Data::StringW(vec![Some(Box::new("invast".to_string()))])
});
message.fields.push(Field {
field_id: 1076,
data: crate::sdo::encode::Data::StringW(vec![Some(Box::new("1".to_string()))])
});
message.fields.push(Field {
field_id: 490, // productFullVersion
data: crate::sdo::encode::Data::StringW(vec![Some(Box::new("22.10.10/2022.14".to_string()))])
});
let bytes = message.encode();
// println!("got: {:x?}", bytes);
assert_eq!(WS_DUMP, bytes);
Ok(())
} */
}