Added support for parsing input streams

This commit is contained in:
2020-11-11 21:21:54 +01:00
parent 3f0aaa259d
commit 659d4cb817
2 changed files with 256 additions and 30 deletions

View File

@@ -36,17 +36,19 @@ fn run(mut session: tn3270::Session) -> anyhow::Result<()> {
WriteOrder::SetBufferAddress(0),
WriteOrder::EraseUnprotectedToAddress(bufsz.last_address()),
WriteOrder::SetBufferAddress(bufsz.encode_address(1, 31)),
WriteOrder::StartFieldExtended(FieldAttribute::PROTECTED, vec![
WriteOrder::StartFieldExtended(vec![
ExtendedFieldAttribute::FieldAttribute(FieldAttribute::PROTECTED),
// ExtendedFieldAttribute::ForegroundColor(Color::Red),
]),
WriteOrder::SendText("Hello from Rust!".into()),
WriteOrder::SetBufferAddress(bufsz.encode_address(8, 21)),
WriteOrder::StartFieldExtended(FieldAttribute::INTENSE_SELECTOR_PEN_DETECTABLE, vec![]),
WriteOrder::StartField(FieldAttribute::INTENSE_SELECTOR_PEN_DETECTABLE),
WriteOrder::SendText(" ".into()),
WriteOrder::StartField(FieldAttribute::PROTECTED),
WriteOrder::SetBufferAddress(bufsz.encode_address(8, 10)),
WriteOrder::StartFieldExtended(FieldAttribute::PROTECTED, vec![
ExtendedFieldAttribute::ForegroundColor(Color::Turquoise),
WriteOrder::StartFieldExtended(vec![
ExtendedFieldAttribute::FieldAttribute(FieldAttribute::PROTECTED),
// ExtendedFieldAttribute::ForegroundColor(Color::Turquoise),
]),
WriteOrder::SendText("Name:".into()),
],
@@ -54,7 +56,8 @@ fn run(mut session: tn3270::Session) -> anyhow::Result<()> {
for (i, line) in rust_logo.iter().enumerate() {
record.orders.push(WriteOrder::SetBufferAddress(bufsz.encode_address(3+i as u16, 31)));
record.orders.push(WriteOrder::StartFieldExtended(FieldAttribute::PROTECTED, vec![
record.orders.push(WriteOrder::StartFieldExtended(vec![
ExtendedFieldAttribute::FieldAttribute(FieldAttribute::PROTECTED),
ExtendedFieldAttribute::ForegroundColor(Color::Red),
]));
record.orders.push(WriteOrder::SendText((*line).into()));
@@ -68,13 +71,25 @@ fn run(mut session: tn3270::Session) -> anyhow::Result<()> {
let record = session.receive_record(None)?;
if let Some(record) = record {
eprintln!("Incoming record: {:?}", hex::encode(record));
eprintln!("Incoming record: {:?}", hex::encode(&record));
eprintln!("Decoded: {:#?}", IncomingRecord::parse_record(record.as_slice()))
} else {
eprintln!("No record");
}
session.send_record(&WriteCommand{
command: WriteCommandCode::Write,
wcc: WCC::RESET_MDT,
orders: vec![
WriteOrder::SetBufferAddress(bufsz.encode_address(8,21)),
WriteOrder::ModifyField(vec![
ExtendedFieldAttribute::FieldAttribute(FieldAttribute::PROTECTED),
]),
],
})?;
std::thread::sleep(Duration::from_secs(5));
std::thread::sleep(Duration::from_secs(50));
Ok(())
}

View File

@@ -1,12 +1,18 @@
use bitflags::bitflags;
use std::io::Write;
use std::convert::TryFrom;
use snafu::Snafu;
use std::convert::{TryFrom, TryInto};
use snafu::{Snafu, ensure};
use hex::encode;
use std::fs::read_to_string;
#[derive(Clone, Debug, Snafu)]
pub enum StreamFormatError {
#[snafu(display("Invalid AID: {:02x}", aid))]
InvalidAID { aid: u8, }
InvalidAID { aid: u8, },
#[snafu(display("Record ended early"))]
UnexpectedEOR,
#[snafu(display("Invalid data"))]
InvalidData,
}
const WCC_TRANS: [u8; 64] = [
@@ -35,12 +41,15 @@ bitflags! {
bitflags! {
pub struct FieldAttribute: u8 {
const HI_1 = 0x40;
const HI_2 = 0x80;
const PROTECTED = 0x20;
const NUMERIC = 0x10;
const NON_DISPLAY = 0x0C;
const DISPLAY_SELECTOR_PEN_DETECTABLE = 0x04;
const INTENSE_SELECTOR_PEN_DETECTABLE = 0x08;
const MODIFIED = 0x01;
const NONE = 0x00;
}
}
@@ -138,24 +147,60 @@ impl Into<u8> for Color {
}
}
impl TryFrom<u8> for Color {
type Error = StreamFormatError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Ok(match value {
0x00 => Color::Default,
0xF0 => Color::NeutralBG,
0xF1 => Color::Blue,
0xF2 => Color::Red,
0xF3 => Color::Pink,
0xF4 => Color::Green,
0xF5 => Color::Turquoise,
0xF6 => Color::Yellow,
0xF7 => Color::NeutralFG,
0xF8 => Color::Black,
0xF9 => Color::DeepBlue,
0xFA => Color::Orange,
0xFB => Color::Purple,
0xFC => Color::PaleGreen,
0xFD => Color::PaleTurquoise,
0xFE => Color::Grey,
0xFF => Color::White,
_ => return Err(StreamFormatError::InvalidData),
})
}
}
#[derive(Copy, Clone, Debug, Hash)]
pub enum Highlighting {
Default,
Normal,
Blink,
Reverse,
Underscore,
Default = 0x00,
Normal = 0xF0,
Blink = 0xF1,
Reverse = 0xF2,
Underscore = 0xF4,
}
impl TryFrom<u8> for Highlighting {
type Error = StreamFormatError;
fn try_from(v: u8) -> Result<Self, Self::Error> {
Ok(match v {
0x00 => Highlighting::Default,
0xF0 => Highlighting::Normal,
0xF1 => Highlighting::Blink,
0xF2 => Highlighting::Reverse,
0xF4 => Highlighting::Underscore,
_ => return Err(StreamFormatError::InvalidData)
})
}
}
impl Into<u8> for Highlighting {
fn into(self) -> u8 {
match self {
Highlighting::Default => 0x00,
Highlighting::Normal => 0xF0,
Highlighting::Blink => 0xF1,
Highlighting::Reverse => 0xF2,
Highlighting::Underscore => 0xF4,
}
self as u8
}
}
@@ -177,9 +222,9 @@ pub enum Transparency {
Opaque,
}
impl Into<u8> for Transparency {
fn into(self) -> u8 {
match self {
impl From<Transparency> for u8 {
fn from(v: Transparency) -> u8 {
match v {
Transparency::Default => 0x00,
Transparency::Or => 0xF0,
Transparency::Xor => 0xF1,
@@ -188,6 +233,20 @@ impl Into<u8> for Transparency {
}
}
impl TryFrom<u8> for Transparency {
type Error = StreamFormatError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Ok(match value {
0x00 => Transparency::Default,
0xF0 => Transparency::Or,
0xF1 => Transparency::Xor,
0xF2 => Transparency::Opaque,
_ => return Err(StreamFormatError::InvalidData)
})
}
}
bitflags! {
pub struct FieldValidation: u8 {
const MANDATORY_FILL = 0b100;
@@ -207,7 +266,27 @@ pub enum ExtendedFieldAttribute {
FieldAttribute(FieldAttribute),
FieldValidation(FieldValidation),
FieldOutlining(FieldOutline),
}
impl TryFrom<&[u8]> for ExtendedFieldAttribute {
type Error = StreamFormatError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
ensure!(value.len() == 2, UnexpectedEOR);
Ok(match (value[0], value[1]) {
(0x00, 0x00) => ExtendedFieldAttribute::AllAttributes,
(0x00, _) => return Err(StreamFormatError::InvalidData),
(0xC0, fa) => ExtendedFieldAttribute::FieldAttribute(FieldAttribute::from_bits(fa & 0x3F).ok_or(StreamFormatError::InvalidData)?),
(0x41, v) => ExtendedFieldAttribute::ExtendedHighlighting(v.try_into()?),
(0x45, v) => ExtendedFieldAttribute::BackgroundColor(v.try_into()?),
(0x42, v) => ExtendedFieldAttribute::ForegroundColor(v.try_into()?),
(0x43, v) => ExtendedFieldAttribute::CharacterSet(v),
(0xC2, v) => ExtendedFieldAttribute::FieldOutlining(FieldOutline::from_bits(v).ok_or(StreamFormatError::InvalidData)?),
(0x46, v) => ExtendedFieldAttribute::Transparency(v.try_into()?),
(0xC1, v) => ExtendedFieldAttribute::FieldValidation(FieldValidation::from_bits(v).ok_or(StreamFormatError::InvalidData)?),
_ => return Err(StreamFormatError::InvalidData),
})
}
}
impl ExtendedFieldAttribute {
@@ -257,7 +336,8 @@ impl BufferAddressCalculator {
#[derive(Clone, Debug)]
pub enum WriteOrder {
StartField(FieldAttribute),
StartFieldExtended(FieldAttribute, Vec<ExtendedFieldAttribute>),
/// The list of attributes MUST include a FieldAttribute
StartFieldExtended(Vec<ExtendedFieldAttribute>),
SetBufferAddress(u16),
SetAttribute(ExtendedFieldAttribute),
ModifyField(Vec<ExtendedFieldAttribute>),
@@ -274,10 +354,9 @@ impl WriteOrder {
pub fn serialize(&self, output: &mut Vec<u8>) {
match self {
WriteOrder::StartField(attr) => output.extend_from_slice(&[0x1D, attr.bits()]),
WriteOrder::StartFieldExtended(attr, rest) => {
output.extend_from_slice(&[0x29, rest.len() as u8 + 1]);
ExtendedFieldAttribute::FieldAttribute(*attr).encode_into(&mut* output);
for attr in rest {
WriteOrder::StartFieldExtended(attrs) => {
output.extend_from_slice(&[0x29, attrs.len() as u8]);
for attr in attrs {
attr.encode_into(&mut *output);
}
}
@@ -329,6 +408,7 @@ impl Into<Vec<u8>> for &WriteCommand {
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum AID {
NoAIDGenerated,
NoAIDGeneratedPrinter,
@@ -467,4 +547,135 @@ impl TryFrom<u8> for AID {
_ => return Err(StreamFormatError::InvalidAID { aid }),
})
}
}
#[derive(Debug, Clone)]
pub struct IncomingRecord {
pub aid: AID,
pub addr: u16,
pub orders: Vec<WriteOrder>,
}
fn parse_addr(encoded: &[u8]) -> Result<u16, StreamFormatError> {
match encoded[0] >> 6 {
0b00 => Ok(((encoded[0] as u16) << 8) + encoded[1] as u16),
0b01 | 0b11 => {
Ok((encoded[0] as u16 & 0x3F) << 6 | (encoded[1] as u16 & 0x3F))
}
_ => Err(StreamFormatError::InvalidData),
}
}
impl IncomingRecord {
pub fn parse_record(mut record: &[u8]) -> Result<Self, StreamFormatError> {
if record.len() < 3 {
return Err(StreamFormatError::UnexpectedEOR);
}
let aid = AID::try_from(record[0])?;
// TODO: Handle AID 88 structured fields
let addr = parse_addr(&record[1..3])?;
let mut result = Self {
aid,
addr,
orders: vec![]
};
record = &record[3..];
while record.len() > 0 {
match record[0] {
0x1D => {
ensure!(record.len() >= 2, UnexpectedEOR);
result.orders.push(
WriteOrder::StartField(FieldAttribute::from_bits(record[1] & 0x3F)
.ok_or(StreamFormatError::InvalidData)?));
record = &record[2..];
},
0x29 => {
ensure!(record.len() >= 2, UnexpectedEOR);
let (header, body) = record.split_at(2);
let count = header[1] as usize;
ensure!(body.len() >= count * 2, UnexpectedEOR);
let (attrs, rest) = body.split_at(2 * count);
record = rest;
result.orders.push(
WriteOrder::StartFieldExtended(
attrs.chunks(2)
.map(ExtendedFieldAttribute::try_from)
.collect::<Result<Vec<ExtendedFieldAttribute>, StreamFormatError>>()?
)
)
}
0x11 => {
ensure!(record.len() >= 3, UnexpectedEOR);
result.orders.push(WriteOrder::SetBufferAddress(parse_addr(&record[1..3])?));
record = &record[3..];
}
0x28 => {
ensure!(record.len() >= 3, UnexpectedEOR);
result.orders.push(WriteOrder::SetAttribute(ExtendedFieldAttribute::try_from(&record[1..3])?));
record = &record[3..];
}
0x2C => {
ensure!(record.len() >= 2, UnexpectedEOR);
let (header, body) = record.split_at(2);
let count = header[1] as usize;
ensure!(body.len() >= count * 2, UnexpectedEOR);
let (attrs, rest) = body.split_at(2 * count);
record = rest;
result.orders.push(
WriteOrder::ModifyField(
attrs.chunks(2)
.map(ExtendedFieldAttribute::try_from)
.collect::<Result<Vec<ExtendedFieldAttribute>, StreamFormatError>>()?
)
)
}
0x13 => {
ensure!(record.len() >= 3, UnexpectedEOR);
result.orders.push(WriteOrder::InsertCursor(parse_addr(&record[1..3])?));
record = &record[3..];
}
0x05 => {
result.orders.push(WriteOrder::ProgramTab);
record = &record[1..];
}
0x3C => {
ensure!(record.len() >= 4, UnexpectedEOR);
// TODO: Handle graphic escape properly
result.orders.push(WriteOrder::RepeatToAddress(
parse_addr(&record[1..3])?,
crate::encoding::cp037::DECODE_TBL[record[4] as usize] as char,
));
record = &record[4..]
}
0x12 => {
ensure!(record.len() >= 3, UnexpectedEOR);
result.orders.push(WriteOrder::EraseUnprotectedToAddress(parse_addr(&record[1..3])?));
record = &record[3..];
}
0x08 => {
ensure!(record.len() >= 2, UnexpectedEOR);
result.orders.push(WriteOrder::GraphicEscape(record[2]));
record = &record[2..];
}
0x40..=0xFF => {
let len = record.iter().position(|&v| v < 0x40).unwrap_or(record.len());
let data = record[..len]
.iter()
.map(|&v| crate::encoding::cp037::DECODE_TBL[v as usize] as char)
.collect();
result.orders.push(WriteOrder::SendText(data));
record = &record[len..];
},
_ => return Err(StreamFormatError::InvalidData)
}
}
Ok(result)
}
}