From e0af47a299ddb6617ac7e6e63441788208283ee5 Mon Sep 17 00:00:00 2001 From: Andrew Kay Date: Sun, 19 Jul 2020 13:48:17 -0500 Subject: [PATCH] EAB feature --- protocol/protocol.md | 75 ++++++++++++++++--------- pycoax/coax/__init__.py | 14 ++++- pycoax/coax/features.py | 29 ++++++++++ pycoax/coax/protocol.py | 90 ++++++++++++++++++++++-------- pycoax/examples/30_get_features.py | 14 +++++ pycoax/examples/40_eab.py | 75 +++++++++++++++++++++++++ pycoax/tests/test_features.py | 69 +++++++++++++++++++++++ pycoax/tests/test_protocol.py | 18 +----- 8 files changed, 318 insertions(+), 66 deletions(-) create mode 100644 pycoax/coax/features.py create mode 100755 pycoax/examples/30_get_features.py create mode 100755 pycoax/examples/40_eab.py create mode 100644 pycoax/tests/test_features.py diff --git a/protocol/protocol.md b/protocol/protocol.md index 5b0679d..595d757 100644 --- a/protocol/protocol.md +++ b/protocol/protocol.md @@ -187,6 +187,22 @@ implemented as a second buffer that shadows the regen buffer; this buffer only contains extended attribute bytes that control the formatting of the characters in the regen buffer - it does not include any characters. +An extended attribute byte is considered an Extended Field Attribute (EFA) if +the byte shadows an attribute byte in the regen buffer, or an Extended +Character Attribute (ECA) if it shadows a character byte. + +| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +| --- |:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| | _M_ | _M_ | _C_ | _C_ | _C_ | _S_ | _S_ | _S_ | + +Bits: + +| Bits | Description | +| --------- | ----------- | +| 7-6 (`M`) | `00` - normal (or most recent EFA)
`01` - blink
`10` - reverse
`11` - underline | +| 5-3 (`C`) | `000` - base color (or most recent EFA)
`001` - blue
`010` - red
`011` - pink
`100` - green
`101` - turquoise
`110` - yellow
`111` - white | +| 2-0 (`S`) | `000` - base (or most recent EFA)
`001` - APL
`010` - PS 2
`011` - PS 3
`100` - PS 4
`101` - PS 5
`110` - PS 6
`111` - PS 7 | + ### Keyboard Keypresses are stored in a FIFO buffer. If there are any keypresses, the scan @@ -217,33 +233,37 @@ Registers can be read-only, write-only, or read-write. ### Commands -| Feature | Command | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Value | -| ------- | ------- |:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-----:| -| Base | `POLL` | _X_ | _X_ | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `0x01` | -| Base | `POLL_ACK` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `1` | `0` | `1` | `0x11` | -| Base | `READ_STATUS` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `1` | `0` | `1` | `0x0d` | -| Base | `READ_TERMINAL_ID` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `1` | `0` | `1` | `0x09` | -| Base | `READ_EXTENDED_ID` | `0` | `0` | `0` | `0` | `0` | `1` | `1` | `1` | `0` | `1` | `0x07` | -| Base | `READ_ADDRESS_COUNTER_HI` | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `1` | `0x05` | -| Base | `READ_ADDRESS_COUNTER_LO` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `1` | `0` | `1` | `0x15` | -| Base | `READ_DATA` | `0` | `0` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `1` | `0x03` | -| Base | `READ_MULTIPLE` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `1` | `0` | `1` | `0x0b` | -| Base | `RESET` | `0` | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `1` | `0x02` | -| Base | `LOAD_CONTROL_REGISTER` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `0` | `1` | `0x0a` | -| Base | `LOAD_SECONDARY_CONTROL` | `0` | `0` | `0` | `1` | `1` | `0` | `1` | `0` | `0` | `1` | `0x1a` | -| Base | `LOAD_MASK` | `0` | `0` | `0` | `1` | `0` | `1` | `1` | `0` | `0` | `1` | `0x16` | -| Base | `LOAD_ADDRESS_COUNTER_HI` | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `1` | `0x04` | -| Base | `LOAD_ADDRESS_COUNTER_LO` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `0` | `0` | `1` | `0x14` | -| Base | `WRITE_DATA` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `0` | `0` | `1` | `0x0c` | -| Base | `CLEAR` | `0` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `0` | `1` | `0x06` | -| Base | `SEARCH_FORWARD` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `0` | `0` | `1` | `0x10` | -| Base | `SEARCH_BACKWARD` | `0` | `0` | `0` | `1` | `0` | `0` | `1` | `0` | `0` | `1` | `0x12` | -| Base | `INSERT_BYTE` | `0` | `0` | `0` | `0` | `1` | `1` | `1` | `0` | `0` | `1` | `0x0e` | -| Base | `START_OPERATION` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `0` | `1` | `0x08` | -| Base | `DIAGNOSTIC_RESET` | `0` | `0` | `0` | `1` | `1` | `1` | `0` | `0` | `0` | `1` | `0x1c` | - -_The hexadecimal value above represents the value of the 8-bit command byte; this is -bits 9-2 shifted two bits to the right._ +| Feature | Command | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +| ------- | ------- |:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| +| Base | `POLL` | _X_ | _X_ | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | +| Base | `POLL_ACK` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `1` | `0` | `1` | +| Base | `READ_STATUS` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `1` | `0` | `1` | +| Base | `READ_TERMINAL_ID` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `1` | `0` | `1` | +| Base | `READ_EXTENDED_ID` | `0` | `0` | `0` | `0` | `0` | `1` | `1` | `1` | `0` | `1` | +| Base | `READ_ADDRESS_COUNTER_HI` | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `1` | +| Base | `READ_ADDRESS_COUNTER_LO` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `1` | `0` | `1` | +| Base | `READ_DATA` | `0` | `0` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `1` | +| Base | `READ_MULTIPLE` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `1` | `0` | `1` | +| Base | `RESET` | `0` | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `1` | +| Base | `LOAD_CONTROL_REGISTER` | `0` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `0` | `1` | +| Base | `LOAD_SECONDARY_CONTROL` | `0` | `0` | `0` | `1` | `1` | `0` | `1` | `0` | `0` | `1` | +| Base | `LOAD_MASK` | `0` | `0` | `0` | `1` | `0` | `1` | `1` | `0` | `0` | `1` | +| Base | `LOAD_ADDRESS_COUNTER_HI` | `0` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `1` | +| Base | `LOAD_ADDRESS_COUNTER_LO` | `0` | `0` | `0` | `1` | `0` | `1` | `0` | `0` | `0` | `1` | +| Base | `WRITE_DATA` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `0` | `0` | `1` | +| Base | `CLEAR` | `0` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `0` | `1` | +| Base | `SEARCH_FORWARD` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `0` | `0` | `1` | +| Base | `SEARCH_BACKWARD` | `0` | `0` | `0` | `1` | `0` | `0` | `1` | `0` | `0` | `1` | +| Base | `INSERT_BYTE` | `0` | `0` | `0` | `0` | `1` | `1` | `1` | `0` | `0` | `1` | +| Base | `START_OPERATION` | `0` | `0` | `0` | `0` | `1` | `0` | `0` | `0` | `0` | `1` | +| Base | `DIAGNOSTIC_RESET` | `0` | `0` | `0` | `1` | `1` | `1` | `0` | `0` | `0` | `1` | +| All | `READ_FEATURE_ID` | _F_ | _F_ | _F_ | _F_ | `0` | `1` | `1` | `1` | `0` | `1` | +| EAB | `READ_DATA` | _F_ | _F_ | _F_ | _F_ | `0` | `0` | `1` | `1` | `0` | `1` | +| EAB | `LOAD_MASK` | _F_ | _F_ | _F_ | _F_ | `0` | `1` | `0` | `1` | `0` | `1` | +| EAB | `WRITE_ALTERNATE` | _F_ | _F_ | _F_ | _F_ | `1` | `0` | `1` | `0` | `0` | `1` | +| EAB | `READ_MULTIPLE` | _F_ | _F_ | _F_ | _F_ | `1` | `0` | `1` | `1` | `0` | `1` | +| EAB | `WRITE_UNDER_MASK` | _F_ | _F_ | _F_ | _F_ | `1` | `1` | `0` | `0` | `0` | `1` | +| EAB | `READ_STATUS` | _F_ | _F_ | _F_ | _F_ | `1` | `1` | `0` | `1` | `0` | `1` | ## References @@ -253,3 +273,4 @@ bits 9-2 shifted two bits to the right._ * NS DP8340 * NS DP8341 * NS DP8344 + * IRMA Technical Reference diff --git a/pycoax/coax/__init__.py b/pycoax/coax/__init__.py index 2fe13ba..9012455 100644 --- a/pycoax/coax/__init__.py +++ b/pycoax/coax/__init__.py @@ -31,7 +31,19 @@ from .protocol import ( search_backward, insert_byte, start_operation, - diagnostic_reset + diagnostic_reset, + read_feature_id, + eab_read_data, + eab_load_mask, + eab_write_alternate, + eab_read_multiple, + eab_write_under_mask, + eab_read_status +) + +from .features import ( + Feature, + get_features ) from .exceptions import ( diff --git a/pycoax/coax/features.py b/pycoax/coax/features.py new file mode 100644 index 0000000..fc4e15b --- /dev/null +++ b/pycoax/coax/features.py @@ -0,0 +1,29 @@ +""" +coax.features +~~~~~~~~~~~~~ +""" + +from enum import Enum + +from .protocol import read_feature_id + +class Feature(Enum): + """Terminal feature.""" + + EAB = 0x79 + +def get_features(interface, **kwargs): + """Get the features a terminal supports.""" + known_ids = set([feature.value for feature in Feature]) + + features = dict() + + for address in range(2, 16): + id_ = read_feature_id(interface, address, **kwargs) + + if id_ is not None and id_ in known_ids: + feature = Feature(id_) + + features[feature] = address + + return features diff --git a/pycoax/coax/protocol.py b/pycoax/coax/protocol.py index b817ac0..4046b09 100644 --- a/pycoax/coax/protocol.py +++ b/pycoax/coax/protocol.py @@ -11,7 +11,7 @@ from .parity import odd_parity class Command(Enum): """Terminal command.""" - # Read Commands + # Base POLL = 0x01 POLL_ACK = 0x11 READ_STATUS = 0x0d @@ -22,7 +22,6 @@ class Command(Enum): READ_DATA = 0x03 READ_MULTIPLE = 0x0b - # Write Commands RESET = 0x02 LOAD_CONTROL_REGISTER = 0x0a LOAD_SECONDARY_CONTROL = 0x1a @@ -37,6 +36,17 @@ class Command(Enum): START_OPERATION = 0x08 DIAGNOSTIC_RESET = 0x1c + # Feature + READ_FEATURE_ID = 0x07 + + # EAB Feature + EAB_READ_DATA = 0x03 + EAB_LOAD_MASK = 0x05 + EAB_WRITE_ALTERNATE = 0x0a + EAB_READ_MULTIPLE = 0x0b + EAB_WRITE_UNDER_MASK = 0x0c + EAB_READ_STATUS = 0x0d + class PollAction(Enum): """Terminal POLL action.""" @@ -326,22 +336,61 @@ def diagnostic_reset(interface): """Execute a DIAGNOSTIC_RESET command.""" raise NotImplementedError -def pack_command_word(command): +def read_feature_id(interface, feature_address, **kwargs): + """Execute a READ_FEATURE_ID command.""" + command_word = pack_command_word(Command.READ_FEATURE_ID, feature_address) + + response = _execute_read_command(interface, command_word, 1, allow_trta_response=True, + **kwargs) + + if response is None: + return None + + return response[0] + +def eab_read_data(interface, feature_address, **kwargs): + """Execute a EAB_READ_DATA command.""" + command_word = pack_command_word(Command.EAB_READ_DATA, feature_address) + + return _execute_read_command(interface, command_word, **kwargs) + +def eab_load_mask(interface, feature_address, mask, **kwargs): + """Execute a EAB_LOAD_MASK command.""" + command_word = pack_command_word(Command.EAB_LOAD_MASK, feature_address) + + _execute_write_command(interface, command_word, bytes([mask]), **kwargs) + +def eab_write_alternate(interface, feature_address, data, **kwargs): + """Execute a EAB_WRITE_ALTERNATE command.""" + command_word = pack_command_word(Command.EAB_WRITE_ALTERNATE, feature_address) + + _execute_write_command(interface, command_word, data, **kwargs) + +def eab_read_multiple(interface, feature_address, **kwargs): + """Execute a EAB_READ_MULTIPLE command.""" + command_word = pack_command_word(Command.EAB_READ_MULTIPLE, feature_address) + + return _execute_read_command(interface, command_word, 32, + validate_response_length=False, **kwargs) + +def eab_write_under_mask(interface, feature_address, byte, **kwargs): + """Execute a EAB_WRITE_UNDER_MASK command.""" + command_word = pack_command_word(Command.EAB_WRITE_UNDER_MASK, feature_address) + + _execute_write_command(interface, command_word, bytes([byte]), **kwargs) + +def eab_read_status(interface, feature_address, **kwargs): + """Execute a EAB_READ_STATUS command.""" + command_word = pack_command_word(Command.EAB_READ_STATUS, feature_address) + + return _execute_read_command(interface, command_word, **kwargs)[0] + +def pack_command_word(command, feature_address=None): """Pack a command into a 10-bit command word.""" - return (command.value << 2) | 0x1 + if feature_address is not None and (feature_address < 2 or feature_address > 15): + raise ValueError(f'Invalid feature address: {feature_address}') -def is_command_word(word): - """Is command word bit set?""" - return (word & 0x1) == 1 - -def unpack_command_word(word): - """Unpack a 10-bit command word.""" - if not is_command_word(word): - raise ProtocolError(f'Word does not have command bit set: {word}') - - command = (word >> 2) & 0x1f - - return Command(command) + return (feature_address << 6 if feature_address is not None else 0) | (command.value << 2) | 0x1 def pack_data_word(byte, set_parity=True): """Pack a data byte into a 10-bit data word.""" @@ -385,10 +434,7 @@ def _execute_read_command(interface, command_word, response_length=1, return trta_value if validate_response_length and len(response) != response_length: - command = unpack_command_word(command_word) - - raise ProtocolError((f'Expected {response_length} word {command.name} ' - f'response: {response}')) + raise ProtocolError((f'Expected {response_length} word response: {response}')) return unpack_data_words(response) if unpack else response @@ -408,9 +454,7 @@ def _execute_write_command(interface, command_word, data=None, **kwargs): receive_length=1, **kwargs) if len(response) != 1: - command = unpack_command_word(command_word) - - raise ProtocolError(f'Expected 1 word {command.name} response: {response}') + raise ProtocolError(f'Expected 1 word response: {response}') if response[0] != 0: raise ProtocolError(f'Expected TR/TA response: {response}') diff --git a/pycoax/examples/30_get_features.py b/pycoax/examples/30_get_features.py new file mode 100755 index 0000000..fac1555 --- /dev/null +++ b/pycoax/examples/30_get_features.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +import sys + +from common import create_serial, create_interface + +from coax import get_features + +with create_serial() as serial: + interface = create_interface(serial) + + features = get_features(interface) + + print(features) diff --git a/pycoax/examples/40_eab.py b/pycoax/examples/40_eab.py new file mode 100755 index 0000000..d0ee785 --- /dev/null +++ b/pycoax/examples/40_eab.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python + +import sys +from itertools import chain + +from common import create_serial, create_interface + +from coax import Feature, get_features, load_address_counter_hi, load_address_counter_lo, write_data, eab_write_alternate, eab_load_mask + +def eab_alternate_zip(regen_buffer, eab_buffer): + return bytes(chain(*zip(regen_buffer, eab_buffer))) + +with create_serial() as serial: + interface = create_interface(serial) + + features = get_features(interface) + + if Feature.EAB not in features: + sys.exit('No EAB feature found.') + + eab_address = features[Feature.EAB] + + print(f'EAB feature found at address {eab_address}') + + # Protected Normal + load_address_counter_hi(interface, 0) + load_address_counter_lo(interface, 80) + + regen_buffer = bytes.fromhex('e0 08 00 af 91 8e 93 84 82 93 84 83 00 ad 8e 91 8c 80 8b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09') + + write_data(interface, regen_buffer) + + # Protected Intense + load_address_counter_hi(interface, 0) + load_address_counter_lo(interface, 160) + + regen_buffer = bytes.fromhex('e8 08 00 af 91 8e 93 84 82 93 84 83 00 a8 8d 93 84 8d 92 84 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09') + + write_data(interface, regen_buffer) + + # Normal EFA + load_address_counter_hi(interface, 1) + load_address_counter_lo(interface, 64) + + regen_buffer = bytes.fromhex('e0 08 00 ad 8e 91 8c 80 8b 00 a4 a5 a0 00 00 00 00 00 00 00 00 00 00 b7 bf 00 a1 bf 00 b1 bf 00 ac bf 00 a6 bf 00 a2 bf 00 b8 bf 00 b6 bf 00 00 09 e0') + eab_buffer = bytes.fromhex('00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 10 00 00 18 00 00 20 00 00 28 00 00 30 00 00 38 00 00 00 00 00') + + eab_write_alternate(interface, eab_address, eab_alternate_zip(regen_buffer, eab_buffer)) + + # Blink EFA + load_address_counter_hi(interface, 1) + load_address_counter_lo(interface, 144) + + regen_buffer = bytes.fromhex('e0 08 00 a1 8b 88 8d 8a 00 a4 a5 a0 00 00 00 00 00 00 00 00 00 00 00 b7 bf 00 a1 bf 00 b1 bf 00 ac bf 00 a6 bf 00 a2 bf 00 b8 bf 00 b6 bf 00 00 09 e0') + eab_buffer = bytes.fromhex('40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 10 00 00 18 00 00 20 00 00 28 00 00 30 00 00 38 00 00 00 00 00') + + eab_write_alternate(interface, eab_address, eab_alternate_zip(regen_buffer, eab_buffer)) + + # Reverse EFA + load_address_counter_hi(interface, 1) + load_address_counter_lo(interface, 224) + + regen_buffer = bytes.fromhex('e0 08 00 b1 84 95 84 91 92 84 00 a4 a5 a0 00 00 00 00 00 00 00 00 00 b7 bf 00 a1 bf 00 b1 bf 00 ac bf 00 a6 bf 00 a2 bf 00 b8 bf 00 b6 bf 00 00 09 e0') + eab_buffer = bytes.fromhex('80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 10 00 00 18 00 00 20 00 00 28 00 00 30 00 00 38 00 00 00 00 00') + + eab_write_alternate(interface, eab_address, eab_alternate_zip(regen_buffer, eab_buffer)) + + # Underline EFA + load_address_counter_hi(interface, 2) + load_address_counter_lo(interface, 48) + + regen_buffer = bytes.fromhex('e0 08 00 b4 8d 83 84 91 8b 88 8d 84 00 a4 a5 a0 00 00 00 00 00 00 00 b7 bf 00 a1 bf 00 b1 bf 00 ac bf 00 a6 bf 00 a2 bf 00 b8 bf 00 b6 bf 00 00 09 e0') + eab_buffer = bytes.fromhex('c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 10 00 00 18 00 00 20 00 00 28 00 00 30 00 00 38 00 00 00 00 00') + + eab_write_alternate(interface, eab_address, eab_alternate_zip(regen_buffer, eab_buffer)) diff --git a/pycoax/tests/test_features.py b/pycoax/tests/test_features.py new file mode 100644 index 0000000..c4e05e4 --- /dev/null +++ b/pycoax/tests/test_features.py @@ -0,0 +1,69 @@ +import unittest +from unittest.mock import Mock, call, patch + +import context + +from coax.features import Feature, get_features + +class GetFeaturesTestCase(unittest.TestCase): + def setUp(self): + self.interface = Mock() + + patcher = patch('coax.features.read_feature_id') + + self.read_feature_id_mock = patcher.start() + + self.addCleanup(patch.stopall) + + def test_with_known_feature(self): + # Arrange + def read_feature_id(interface, feature_address, **kwargs): + if feature_address == 7: + return 0x79 + + return None + + self.read_feature_id_mock.side_effect = read_feature_id + + # Act + features = get_features(self.interface) + + # Assert + self.assertEqual(features, { Feature.EAB: 7 }) + + def test_with_unknown_feature(self): + # Arrange + def read_feature_id(interface, feature_address, **kwargs): + if feature_address == 7: + return 0x99 + + return None + + self.read_feature_id_mock.side_effect = read_feature_id + + # Act + features = get_features(self.interface) + + # Assert + self.assertEqual(features, { }) + + def test_all_feature_addresses_are_enumerated(self): + # Act + features = get_features(self.interface) + + # Assert + calls = self.read_feature_id_mock.call_args_list + + self.assertEqual(calls, [call(self.interface, address) for address in range(2, 16)]) + + def test_receive_timeout_is_passed_to_read_feature_id(self): + # Act + features = get_features(self.interface, receive_timeout=10) + + # Assert + calls = self.read_feature_id_mock.call_args_list + + self.assertEqual(calls, [call(self.interface, address, receive_timeout=10) for address in range(2, 16)]) + +if __name__ == '__main__': + unittest.main() diff --git a/pycoax/tests/test_protocol.py b/pycoax/tests/test_protocol.py index 2cfe4c1..112f981 100644 --- a/pycoax/tests/test_protocol.py +++ b/pycoax/tests/test_protocol.py @@ -4,7 +4,7 @@ from unittest.mock import Mock import context from coax import PollResponse, KeystrokePollResponse, ProtocolError -from coax.protocol import Command, Status, TerminalType, TerminalId, Control, SecondaryControl, pack_command_word, unpack_command_word, pack_data_word, unpack_data_word, pack_data_words, unpack_data_words, _execute_read_command, _execute_write_command +from coax.protocol import Command, Status, TerminalType, TerminalId, Control, SecondaryControl, pack_command_word, pack_data_word, unpack_data_word, pack_data_words, unpack_data_words, _execute_read_command, _execute_write_command class PollResponseTestCase(unittest.TestCase): def test_is_power_on_reset_complete(self): @@ -108,18 +108,6 @@ class PackCommandWordTestCase(unittest.TestCase): def test(self): self.assertEqual(pack_command_word(Command.POLL_ACK), 0b0001000101) -class UnpackCommandWordTestCase(unittest.TestCase): - def test(self): - # Act - command = unpack_command_word(0b0001000101) - - # Assert - self.assertEqual(command, Command.POLL_ACK) - - def test_command_bit_not_set_error(self): - with self.assertRaisesRegex(ProtocolError, 'Word does not have command bit set'): - unpack_command_word(0b0001000100) - class PackDataWordTestCase(unittest.TestCase): def test(self): self.assertEqual(pack_data_word(0x00), 0b0000000010) @@ -190,7 +178,7 @@ class ExecuteReadCommandTestCase(unittest.TestCase): self.interface.transmit_receive = Mock(return_value=[]) # Act and assert - with self.assertRaisesRegex(ProtocolError, 'Expected 1 word READ_TERMINAL_ID response'): + with self.assertRaisesRegex(ProtocolError, 'Expected 1 word response'): _execute_read_command(self.interface, command_word) def test_receive_timeout_is_passed_to_interface(self): @@ -225,7 +213,7 @@ class ExecuteWriteCommandTestCase(unittest.TestCase): self.interface.transmit_receive = Mock(return_value=[]) # Act and assert - with self.assertRaisesRegex(ProtocolError, 'Expected 1 word WRITE_DATA response'): + with self.assertRaisesRegex(ProtocolError, 'Expected 1 word response'): _execute_write_command(self.interface, command_word, bytes.fromhex('de ad be ef')) def test_not_trta_response(self):