import abc
import contextlib
import time
from tbot.machine import channel, connector, linux
__all__ = (
"KermitConnector",
"PicocomConnector",
)
[docs]class KermitConnector(connector.ConsoleConnector):
"""
Connect to a serial console using kermit
You can configure the device name using the ``kermit_cfg_file`` property.
**Example**: (board config)
.. code-block:: python
from tbot.machine import board
from tbottest.connector import KermitConnector
class MyBoard(KermitConnector, board.Board):
kermit_cfg_file = "path to config file"
BOARD = MyBoard
"""
@property
def kermit_delay(self) -> float:
"""
delay after exit, default None
"""
return 0.0
@property
@abc.abstractmethod
def kermit_cfg_file(self) -> str:
"""
kermit config file
This property is **required**.
"""
raise Exception("abstract method")
@contextlib.contextmanager
def kermitconnect(self, mach: linux.LinuxShell) -> channel.Channel:
KERMIT_PROMPT = b"C-Kermit>"
ch = mach.open_channel("kermit", self.kermit_cfg_file)
try:
try:
ret = ch.read(150, timeout=2)
buf = ret.decode(errors="replace")
if "Locked" in buf:
raise RuntimeError(f"serial line is locked {buf}")
except TimeoutError:
pass
yield ch
finally:
ch.sendcontrol("\\")
ch.send("C")
ch.read_until_prompt(KERMIT_PROMPT)
ch.sendline("exit")
# get original prompt...
if self.kermit_delay != 0.0:
time.sleep(self.kermit_delay)
def connect(self, mach: linux.LinuxShell) -> channel.Channel:
return self.kermitconnect(mach)
[docs]class PicocomConnector(connector.ConsoleConnector):
"""
Connect to a serial console using picocom
**Example**: (board config)
.. code-block:: python
from tbot.machine import board
from tbottest.connector import PicocomConnector
class MyBoard(PicocomConnector, board.Board):
baudrate = 115200
device = /dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller-if00-port0
noreset
BOARD = MyBoard
"""
@property
def delay(self) -> float:
"""
delay after exit, default None
"""
return 0.0
@property
@abc.abstractmethod
def baudrate(self) -> str:
"""
baudrate (picocom argument -b)
This property is **required**.
"""
raise Exception("abstract method")
@property
@abc.abstractmethod
def device(self) -> str:
"""
tty device (argument -l)
This property is **required**.
"""
raise Exception("picocom abstract method")
@property
def noreset(self) -> bool:
"""
Pass -r picocom argument
picocom manual says:
If given, picocom will not reset the serial port when exiting.
It will just close the respective filedescriptor and do nothing more.
The serial port settings will not be restored to their original values and,
unless the --hangup option is also given, the modem-control lines will not
beaffected. This is useful, for example, for leaving modems connected when
exiting picocom. Regardless whether the --noreset option is given, the user
can exit picocom using the "Quit"command (instead of "Exit"), which makes
picocom behave exactly as if --noreset was given. See also the --hangup option.
(Default: Disabled)
NOTICE: Picocom clears the modem control lines on exit by setting the HUPCL
control bit of therespective port. Picocom always sets HUPCL according to the
--noreset and --hangup options. If --noreset is given and --hangup is not, then
HUPCL for the port is cleared and will remain soafter exiting picocom.
If --noreset is not given, or if both --noreset and --hangup are given, then
HUPCL is set for the port and will remain so after exiting picocom. This is
true, regardless ofthe way picocom terminates (command, read zero-bytes from
standard input, killed by signal,fatal error, etc), and regardless of the
--noinit option.
"""
return False
@contextlib.contextmanager
def picocomconnect(self, mach: linux.LinuxShell) -> channel.Channel:
args = []
if self.noreset:
args.append("-r")
args.append("-b")
args.append(self.baudrate)
args.append("-l")
args.append(self.device)
ch = mach.open_channel("picocom", *args)
try:
yield ch
finally:
ch.sendcontrol("A")
ch.sendcontrol("Q")
# some usb adapters need here an delay...
if self.delay != 0.0:
time.sleep(self.delay)
def connect(self, mach: linux.LinuxShell) -> channel.Channel:
return self.picocomconnect(mach)
[docs]class ScriptConnector(connector.ConsoleConnector):
"""
Connect to a serial console using a script
**Example**: (board config)
.. code-block:: python
from tbot.machine import board
from tbottest.connector import PicocomConnector
class MyBoard(ScriptConnector, board.Board):
scriptname = "connect"
exitstring = ~~.
boardname = wandboard
BOARD = MyBoard
"""
@property
@abc.abstractmethod
def boardname(self) -> str:
"""
Name of the board
This property is **required**.
"""
raise Exception("abstract method")
@property
@abc.abstractmethod
def exitstring(self) -> str:
"""
string send to exit
This property is **required**.
"""
raise Exception("abstract method")
@property
@abc.abstractmethod
def scriptname(self) -> str:
"""
Name of the script
This property is **required**.
"""
raise Exception("abstract method")
@contextlib.contextmanager
def scriptconnect(self, mach: linux.LinuxShell, boardname) -> channel.Channel:
try:
ch = mach.open_channel(self.scriptname, boardname)
yield ch
except Exception as ex:
print("Exception ", ex)
finally:
ch.send(self.exitstring)
def connect(self, mach: linux.LinuxShell) -> channel.Channel:
return self.scriptconnect(mach, self.boardname)