Detect unsupported DFT terminals

This commit is contained in:
Andrew Kay 2020-05-12 20:24:08 -05:00
parent 52a05ad1df
commit c1b8dc11c4
5 changed files with 50 additions and 19 deletions

View File

@ -1,22 +1,31 @@
# oec
IBM 3270 terminal controller - an open replacement for the IBM 3174 Establishment Controller.
IBM 3270 terminal controller - a replacement for the IBM 3174.
![IBM 3278 terminal and oec](.images/hero.jpg)
## Features
This is a work in progress - as of now it only provides basic TN3270 and VT100 emulation.
The goal of this project is to create an open replacement for the IBM 3174 Establishment Controller, specifically for users looking to connect a IBM 3270 type terminal to the Hercules emulator. It is a work in progress and is far from providing all the features of the 3174, but it does provide basic TN3270 and VT100 emulation.
- [x] TN3270
- [x] Basic TN3270
- [ ] EAB
- [ ] TN3270E
- [ ] SSL/TLS
- [ ] Non-English character sets
- [x] VT100
- [ ] Connection menu
- [ ] Multiple logical terminals
- [x] TN3270
- [x] Basic TN3270
- [ ] TN3270E
- [ ] EAB (Extended Attribute Buffer)
- [ ] SSL/TLS
- [ ] Non-English character sets
- [x] VT100
- [ ] Connection menu
- [ ] MLT (Multiple Logical Terminals)
## Supported Terminals
Only CUT (Control Unit Terminal) type terminals are supported. I have tested oec with the following terminals:
* IBM 3278-2
* IBM 3483-V (InfoWindow II)
You may have to modify the key mapping to support your specific terminal configuration.
## Usage
@ -27,7 +36,7 @@ Then configure a Python virtual environment and install dependencies:
```
python -m venv VIRTUALENV
. VIRTUALENV/bin/activate
pip install -r requirements.txt --no-deps
pip install -r requirements.txt
```
Assuming your interface is connected to `/dev/ttyACM0` and you want to connect to a TN3270 host named `mainframe`:

View File

@ -6,9 +6,10 @@ oec.controller
import time
import logging
from coax import poll, poll_ack, load_control_register, PollAction, \
KeystrokePollResponse, ReceiveTimeout, ReceiveError, ProtocolError
KeystrokePollResponse, TerminalType, ReceiveTimeout, \
ReceiveError, ProtocolError
from .terminal import Terminal, read_terminal_ids
from .terminal import Terminal, UnsupportedTerminalError, read_terminal_ids
from .keyboard import Key
from .session import SessionDisconnectedError
@ -76,7 +77,11 @@ class Controller:
return
if not self.terminal:
self._handle_terminal_attached(poll_response)
try:
self._handle_terminal_attached(poll_response)
except UnsupportedTerminalError as error:
self.logger.error(f'Unsupported terminal: {error}')
return
if poll_response:
self._handle_poll_response(poll_response)
@ -89,6 +94,9 @@ class Controller:
self.logger.info(f'Terminal ID = {terminal_id}, Extended ID = {extended_id}')
if terminal_id.type != TerminalType.CUT:
raise UnsupportedTerminalError('Only CUT type terminals are supported')
# Get the keymap.
keymap = self.get_keymap(terminal_id, extended_id)

View File

@ -93,3 +93,6 @@ class Terminal:
cursor_blink=self.display.cursor_blink)
return control
class UnsupportedTerminalError(Exception):
"""Unsupported terminal."""

View File

@ -1,8 +1,8 @@
ptyprocess==0.6.0
pycoax==0.3.1
pycoax==0.4.0
pyserial==3.4
pyte==0.8.0
pytn3270==0.5.0
sliplib==0.3.0
sliplib==0.5.0
sortedcontainers==2.1.0
wcwidth==0.1.7

View File

@ -10,7 +10,8 @@ from oec.session import SessionDisconnectedError
from oec.keyboard import KeyboardModifiers, Key
from oec.keymap_3278_2 import KEYMAP as KEYMAP_3278_2
TERMINAL_IDS = (TerminalId(0b11110100), 'c1348300')
CUT_TERMINAL_IDS = (TerminalId(0b11110100), 'c1348300')
DFT_TERMINAL_IDS = (TerminalId(0b00000001), None)
class RunLoopTestCase(unittest.TestCase):
def setUp(self):
@ -21,6 +22,8 @@ class RunLoopTestCase(unittest.TestCase):
self.controller = Controller(self.interface, lambda terminal_id, extended_id: KEYMAP_3278_2, self.create_session_mock)
self.controller.logger = Mock()
self.controller.connected_poll_period = 1
patcher = patch('oec.controller.poll')
@ -35,7 +38,7 @@ class RunLoopTestCase(unittest.TestCase):
self.read_terminal_ids_mock = patcher.start()
self.read_terminal_ids_mock.return_value = TERMINAL_IDS
self.read_terminal_ids_mock.return_value = CUT_TERMINAL_IDS
patcher = patch('oec.controller.load_control_register')
@ -80,6 +83,14 @@ class RunLoopTestCase(unittest.TestCase):
self.controller.session.handle_host.assert_called()
def test_unsupported_terminal_attached(self):
self.read_terminal_ids_mock.return_value = DFT_TERMINAL_IDS
self._assert_run_loop(0, PowerOnResetCompletePollResponse(0xa), 0, True)
self.assertIsNone(self.controller.terminal)
self.assertIsNone(self.controller.session)
def test_keystroke(self):
self._assert_run_loop(0, PowerOnResetCompletePollResponse(0xa), 0, True)
self._assert_run_loop(0, KeystrokePollResponse(0b0110000010), 0, True)