Refactored write orders to be independent objects, to support parsing reads
This commit is contained in:
@@ -13,3 +13,4 @@ anyhow = "1.0.33"
|
|||||||
thiserror = "1.0.21"
|
thiserror = "1.0.21"
|
||||||
bitflags = "1.2.1"
|
bitflags = "1.2.1"
|
||||||
hex = "0.4.2"
|
hex = "0.4.2"
|
||||||
|
snafu = "0.6.9"
|
||||||
@@ -2,6 +2,7 @@ use structopt::StructOpt;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use tn3270s::tn3270;
|
use tn3270s::tn3270;
|
||||||
|
use tn3270s::tn3270::stream::WriteOrder::SetBufferAddress;
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
@@ -12,16 +13,58 @@ pub struct Cli {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// _~^~^~_
|
||||||
|
// \) / o o \ (/
|
||||||
|
// '_ ¬ _'
|
||||||
|
// / '-----' \
|
||||||
|
// 1234567890123456
|
||||||
|
|
||||||
|
static rust_logo: [&'static str; 4] = [
|
||||||
|
r#" _~^~^~_ "#,
|
||||||
|
r#" \) / o o \ (/ "#,
|
||||||
|
r#" '_ ¬ _' "#,
|
||||||
|
r#" / '-----' \ "#,
|
||||||
|
];
|
||||||
|
|
||||||
fn run(mut session: tn3270::Session) -> anyhow::Result<()> {
|
fn run(mut session: tn3270::Session) -> anyhow::Result<()> {
|
||||||
use tn3270::stream::*;
|
use tn3270::stream::*;
|
||||||
let mut record = WriteCommand::new(WriteCommandCode::Write, WCC::RESET | WCC::KBD_RESTORE);
|
|
||||||
let bufsz = BufferAddressCalculator { width: 80, height: 24 };
|
let bufsz = BufferAddressCalculator { width: 80, height: 24 };
|
||||||
record.set_buffer_address(0)
|
let mut record = WriteCommand {
|
||||||
.erase_unprotected_to_address(bufsz.encode_address(79, 23))
|
command: WriteCommandCode::Write,
|
||||||
.set_buffer_address(bufsz.encode_address(31,1))
|
wcc: WCC::RESET | WCC::KBD_RESTORE | WCC::RESET_MDT,
|
||||||
.set_attribute(ExtendedFieldAttribute::ForegroundColor(Color::Red))
|
orders: vec![
|
||||||
.send_text("Hello from Rust!");
|
WriteOrder::SetBufferAddress(0),
|
||||||
session.send_record(record)?;
|
WriteOrder::EraseUnprotectedToAddress(bufsz.last_address()),
|
||||||
|
WriteOrder::SetBufferAddress(bufsz.encode_address(1, 31)),
|
||||||
|
WriteOrder::StartFieldExtended(FieldAttribute::PROTECTED, vec![
|
||||||
|
// 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::SendText(" ".into()),
|
||||||
|
WriteOrder::StartField(FieldAttribute::PROTECTED),
|
||||||
|
WriteOrder::SetBufferAddress(bufsz.encode_address(8, 10)),
|
||||||
|
WriteOrder::StartFieldExtended(FieldAttribute::PROTECTED, vec![
|
||||||
|
ExtendedFieldAttribute::ForegroundColor(Color::Turquoise),
|
||||||
|
]),
|
||||||
|
WriteOrder::SendText("Name:".into()),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
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![
|
||||||
|
ExtendedFieldAttribute::ForegroundColor(Color::Red),
|
||||||
|
]));
|
||||||
|
record.orders.push(WriteOrder::SendText((*line).into()));
|
||||||
|
}
|
||||||
|
session.send_record(&record)?;
|
||||||
|
session.send_record(&WriteCommand{
|
||||||
|
command: WriteCommandCode::Write,
|
||||||
|
wcc: WCC::RESET_MDT | WCC::KBD_RESTORE,
|
||||||
|
orders: vec![],
|
||||||
|
})?;
|
||||||
|
|
||||||
let record = session.receive_record(None)?;
|
let record = session.receive_record(None)?;
|
||||||
if let Some(record) = record {
|
if let Some(record) = record {
|
||||||
@@ -31,7 +74,7 @@ fn run(mut session: tn3270::Session) -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::thread::sleep(Duration::from_secs(60));
|
std::thread::sleep(Duration::from_secs(5));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
mod cp037;
|
pub(crate) mod cp037;
|
||||||
|
|
||||||
pub trait SBCS {
|
pub trait SBCS {
|
||||||
fn from_unicode(ch: char) -> Option<u8>;
|
fn from_unicode(ch: char) -> Option<u8>;
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ impl Session {
|
|||||||
self.stream.set_nonblocking(true)?;
|
self.stream.set_nonblocking(true)?;
|
||||||
while len != 0 {
|
while len != 0 {
|
||||||
let events = self.parser.receive(&buf[..len]);
|
let events = self.parser.receive(&buf[..len]);
|
||||||
self.process_events(events);
|
self.process_events(events)?;
|
||||||
len = match self.stream.read(buf.as_mut_slice()) {
|
len = match self.stream.read(buf.as_mut_slice()) {
|
||||||
Ok(len) => len,
|
Ok(len) => len,
|
||||||
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => 0,
|
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => 0,
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use snafu::Snafu;
|
||||||
|
|
||||||
static WCC_TRANS: [u8; 64] = [
|
#[derive(Clone, Debug, Snafu)]
|
||||||
|
pub enum StreamFormatError {
|
||||||
|
#[snafu(display("Invalid AID: {:02x}", aid))]
|
||||||
|
InvalidAID { aid: u8, }
|
||||||
|
}
|
||||||
|
|
||||||
|
const WCC_TRANS: [u8; 64] = [
|
||||||
0x40, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
|
0x40, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
|
||||||
0xC8, 0xC9, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
|
0xC8, 0xC9, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
|
||||||
0x50, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
|
0x50, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
|
||||||
@@ -57,7 +65,9 @@ pub trait OutputRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct WriteCommand {
|
pub struct WriteCommand {
|
||||||
data: Vec<u8>,
|
pub command: WriteCommandCode,
|
||||||
|
pub wcc: WCC,
|
||||||
|
pub orders: Vec<WriteOrder>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
@@ -214,6 +224,18 @@ impl ExtendedFieldAttribute {
|
|||||||
ExtendedFieldAttribute::FieldValidation(v) => (0xC1, v.bits()),
|
ExtendedFieldAttribute::FieldValidation(v) => (0xC1, v.bits()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn encode_into(&self, output: &mut Vec<u8>) {
|
||||||
|
let (typ, val) = self.encoded();
|
||||||
|
output.extend_from_slice(&[typ, val]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ExtendedFieldAttribute> for &ExtendedFieldAttribute {
|
||||||
|
fn into(self) -> ExtendedFieldAttribute {
|
||||||
|
*self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
@@ -223,118 +245,226 @@ pub struct BufferAddressCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BufferAddressCalculator {
|
impl BufferAddressCalculator {
|
||||||
pub fn encode_address(self, x: u16, y: u16) -> u16 {
|
pub fn encode_address(self, y: u16, x: u16) -> u16 {
|
||||||
self.width * y + x
|
self.width * y + x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn last_address(self) -> u16 {
|
||||||
|
self.width * self.height - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum WriteOrder {
|
||||||
|
StartField(FieldAttribute),
|
||||||
|
StartFieldExtended(FieldAttribute, Vec<ExtendedFieldAttribute>),
|
||||||
|
SetBufferAddress(u16),
|
||||||
|
SetAttribute(ExtendedFieldAttribute),
|
||||||
|
ModifyField(Vec<ExtendedFieldAttribute>),
|
||||||
|
InsertCursor(u16),
|
||||||
|
ProgramTab,
|
||||||
|
RepeatToAddress(u16, char),
|
||||||
|
EraseUnprotectedToAddress(u16),
|
||||||
|
GraphicEscape(u8),
|
||||||
|
SendText(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
attr.encode_into(&mut *output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WriteOrder::SetBufferAddress(addr) => output.extend_from_slice(&[0x11, (addr >> 8) as u8, (addr & 0xff) as u8]),
|
||||||
|
WriteOrder::SetAttribute(attr) => {
|
||||||
|
let (typ, val) = attr.encoded();
|
||||||
|
output.extend_from_slice(&[0x28, typ, val]);
|
||||||
|
}
|
||||||
|
WriteOrder::ModifyField(attrs) => {
|
||||||
|
output.extend_from_slice(&[0x2C, attrs.len() as u8]);
|
||||||
|
for attr in attrs {
|
||||||
|
attr.encode_into(&mut* output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WriteOrder::InsertCursor(addr) => output.extend_from_slice(&[0x11, (addr >> 8) as u8, (addr & 0xff) as u8]),
|
||||||
|
WriteOrder::ProgramTab => output.push(0x05),
|
||||||
|
WriteOrder::RepeatToAddress(addr, ch) => {
|
||||||
|
// TODO: COme up with a way to allow graphic escape here
|
||||||
|
output.extend_from_slice(&[0x3C, (addr >> 8) as u8, (addr & 0xff) as u8, crate::encoding::cp037::ENCODE_TBL[*ch as usize]])
|
||||||
|
}
|
||||||
|
WriteOrder::EraseUnprotectedToAddress(addr) => {
|
||||||
|
output.extend_from_slice(&[0x12, (addr >> 8) as u8, (addr & 0xff) as u8])
|
||||||
|
}
|
||||||
|
WriteOrder::GraphicEscape(ch) => output.extend_from_slice(&[0x08, *ch]),
|
||||||
|
WriteOrder::SendText(text) => {
|
||||||
|
output.extend(crate::encoding::to_cp037(text.chars()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteCommand {
|
impl WriteCommand {
|
||||||
pub fn new(command: WriteCommandCode, wcc: WCC) -> Self {
|
pub fn serialize(&self, output: &mut Vec<u8>) {
|
||||||
WriteCommand {
|
output.push(self.command.to_command_code());
|
||||||
data: vec![command.to_command_code(), wcc.to_ascii_compat(), ]
|
output.push(self.wcc.to_ascii_compat());
|
||||||
|
for order in self.orders.iter() {
|
||||||
|
order.serialize(&mut *output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_field(&mut self, fa: FieldAttribute) -> &mut Self {
|
|
||||||
self.data.push(0x1D);
|
|
||||||
self.data.push(fa.bits());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start_field_extended(&mut self, fa: FieldAttribute, attrs: impl IntoIterator<Item=ExtendedFieldAttribute>) -> &mut Self {
|
impl Into<Vec<u8>> for &WriteCommand {
|
||||||
self.data.push(0x29);
|
|
||||||
let nattr_pos = self.data.len();
|
|
||||||
self.data.push(0);
|
|
||||||
let mut i = 1;
|
|
||||||
self.data.push(0xC0);
|
|
||||||
self.data.push(make_ascii_translatable(fa.bits()));
|
|
||||||
|
|
||||||
for (typ, value) in attrs.into_iter().map(ExtendedFieldAttribute::encoded) {
|
|
||||||
self.data.push(typ);
|
|
||||||
self.data.push(value);
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
self.data[nattr_pos] = i;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_buffer_address(&mut self, address: u16) -> &mut Self {
|
|
||||||
self.data.push(0x11);
|
|
||||||
self.data.push((address >> 8) as u8);
|
|
||||||
self.data.push((address & 0xFF) as u8);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_attribute(&mut self, attr: ExtendedFieldAttribute) -> &mut Self {
|
|
||||||
let (typ, val) = attr.encoded();
|
|
||||||
self.data.extend_from_slice(&[0x28, typ, val]);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn modify_field(&mut self, attrs: impl IntoIterator<Item=ExtendedFieldAttribute>) -> &mut Self {
|
|
||||||
self.data.push(0x2C);
|
|
||||||
let nattr_pos = self.data.len();
|
|
||||||
self.data.push(0);
|
|
||||||
let mut i = 0;
|
|
||||||
for (typ, value) in attrs.into_iter().map(ExtendedFieldAttribute::encoded) {
|
|
||||||
self.data.push(typ);
|
|
||||||
self.data.push(value);
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
self.data[nattr_pos] = i;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_address(&mut self, address: u16) {
|
|
||||||
self.data.push((address >> 8) as u8);
|
|
||||||
self.data.push((address & 0xFF) as u8);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_cursor(&mut self, address: u16) -> &mut Self {
|
|
||||||
self.data.push(0x13);
|
|
||||||
self.encode_address(address);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn program_tab(&mut self) -> &mut Self {
|
|
||||||
self.data.push(0x05);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// This must be followed by either a character or a graphic escape
|
|
||||||
pub fn repeat_to_address(&mut self, address: u16) -> &mut Self {
|
|
||||||
self.data.push(0x3C);
|
|
||||||
self.encode_address(address);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn erase_unprotected_to_address(&mut self, address: u16) -> &mut Self {
|
|
||||||
self.data.push(0x12);
|
|
||||||
self.encode_address(address);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn graphic_escape(&mut self, charcode: u8) -> &mut Self {
|
|
||||||
self.data.push(0x08);
|
|
||||||
self.data.push(charcode);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_text(&mut self, data: &str) -> &mut Self {
|
|
||||||
self.data.extend(crate::encoding::to_cp037(data.chars()));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for WriteCommand {
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
|
||||||
self.data.as_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Vec<u8>> for WriteCommand {
|
|
||||||
fn into(self) -> Vec<u8> {
|
fn into(self) -> Vec<u8> {
|
||||||
self.data
|
let mut result = vec![];
|
||||||
|
self.serialize(&mut result);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum AID {
|
||||||
|
NoAIDGenerated,
|
||||||
|
NoAIDGeneratedPrinter,
|
||||||
|
StructuredField,
|
||||||
|
ReadPartition,
|
||||||
|
TriggerAction,
|
||||||
|
SysReq,
|
||||||
|
PF1,
|
||||||
|
PF2,
|
||||||
|
PF3,
|
||||||
|
PF4,
|
||||||
|
PF5,
|
||||||
|
PF6,
|
||||||
|
PF7,
|
||||||
|
PF8,
|
||||||
|
PF9,
|
||||||
|
PF10,
|
||||||
|
PF11,
|
||||||
|
PF12,
|
||||||
|
PF13,
|
||||||
|
PF14,
|
||||||
|
PF15,
|
||||||
|
PF16,
|
||||||
|
PF17,
|
||||||
|
PF18,
|
||||||
|
PF19,
|
||||||
|
PF20,
|
||||||
|
PF21,
|
||||||
|
PF22,
|
||||||
|
PF23,
|
||||||
|
PF24,
|
||||||
|
PA1,
|
||||||
|
PA2,
|
||||||
|
PA3,
|
||||||
|
Clear,
|
||||||
|
ClearPartition,
|
||||||
|
Enter,
|
||||||
|
SelectorPenAttention,
|
||||||
|
MagReaderOperatorID,
|
||||||
|
MagReaderNumber,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AID> for u8 {
|
||||||
|
fn from(aid: AID) -> u8 {
|
||||||
|
use self::AID::*;
|
||||||
|
match aid {
|
||||||
|
NoAIDGenerated => 0x60,
|
||||||
|
NoAIDGeneratedPrinter => 0xE8,
|
||||||
|
StructuredField => 0x88,
|
||||||
|
ReadPartition => 0x61,
|
||||||
|
TriggerAction => 0x7f,
|
||||||
|
SysReq => 0xf0,
|
||||||
|
PF1 => 0xF1,
|
||||||
|
PF2 => 0xF2,
|
||||||
|
PF3 => 0xF3,
|
||||||
|
PF4 => 0xF4,
|
||||||
|
PF5 => 0xF5,
|
||||||
|
PF6 => 0xF6,
|
||||||
|
PF7 => 0xF7,
|
||||||
|
PF8 => 0xF8,
|
||||||
|
PF9 => 0xF9,
|
||||||
|
PF10 => 0x7A,
|
||||||
|
PF11 => 0x7B,
|
||||||
|
PF12 => 0x7C,
|
||||||
|
PF13 => 0xC1,
|
||||||
|
PF14 => 0xC2,
|
||||||
|
PF15 => 0xC3,
|
||||||
|
PF16 => 0xC4,
|
||||||
|
PF17 => 0xC5,
|
||||||
|
PF18 => 0xC6,
|
||||||
|
PF19 => 0xC7,
|
||||||
|
PF20 => 0xC8,
|
||||||
|
PF21 => 0xC9,
|
||||||
|
PF22 => 0x4A,
|
||||||
|
PF23 => 0x4B,
|
||||||
|
PF24 => 0x4C,
|
||||||
|
PA1 => 0x6C,
|
||||||
|
PA2 => 0x6E,
|
||||||
|
PA3 => 0x6B,
|
||||||
|
Clear => 0x6D,
|
||||||
|
ClearPartition => 0x6A,
|
||||||
|
Enter => 0x7D,
|
||||||
|
SelectorPenAttention => 0x7E,
|
||||||
|
MagReaderOperatorID => 0xE6,
|
||||||
|
MagReaderNumber => 0xE7,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl TryFrom<u8> for AID {
|
||||||
|
type Error = StreamFormatError;
|
||||||
|
|
||||||
|
fn try_from(aid: u8) -> Result<Self, Self::Error> {
|
||||||
|
use self::AID::*;
|
||||||
|
Ok(match aid {
|
||||||
|
0x60 => NoAIDGenerated,
|
||||||
|
0xE8 => NoAIDGeneratedPrinter,
|
||||||
|
0x88 => StructuredField,
|
||||||
|
0x61 => ReadPartition,
|
||||||
|
0x7f => TriggerAction,
|
||||||
|
0xf0 => SysReq,
|
||||||
|
0xF1 => PF1,
|
||||||
|
0xF2 => PF2,
|
||||||
|
0xF3 => PF3,
|
||||||
|
0xF4 => PF4,
|
||||||
|
0xF5 => PF5,
|
||||||
|
0xF6 => PF6,
|
||||||
|
0xF7 => PF7,
|
||||||
|
0xF8 => PF8,
|
||||||
|
0xF9 => PF9,
|
||||||
|
0x7A => PF10,
|
||||||
|
0x7B => PF11,
|
||||||
|
0x7C => PF12,
|
||||||
|
0xC1 => PF13,
|
||||||
|
0xC2 => PF14,
|
||||||
|
0xC3 => PF15,
|
||||||
|
0xC4 => PF16,
|
||||||
|
0xC5 => PF17,
|
||||||
|
0xC6 => PF18,
|
||||||
|
0xC7 => PF19,
|
||||||
|
0xC8 => PF20,
|
||||||
|
0xC9 => PF21,
|
||||||
|
0x4A => PF22,
|
||||||
|
0x4B => PF23,
|
||||||
|
0x4C => PF24,
|
||||||
|
0x6C => PA1,
|
||||||
|
0x6E => PA2,
|
||||||
|
0x6B => PA3,
|
||||||
|
0x6D => Clear,
|
||||||
|
0x6A => ClearPartition,
|
||||||
|
0x7D => Enter,
|
||||||
|
0x7E => SelectorPenAttention,
|
||||||
|
0xE6 => MagReaderOperatorID,
|
||||||
|
0xE7 => MagReaderNumber,
|
||||||
|
_ => return Err(StreamFormatError::InvalidAID { aid }),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user