Use Command Interpreter Wrappers#
In Using an Adapter we saw how to use a composed adapter with the amplifier
device so that it could receive commands of the form A?
and A=4.4
to get
and set values to the device. These commands had to be sent exactly as specified
and individually. However, when talking to a simulated device this may not be the
case. Our simulated device may receive more complex messages and as a result our
command interpreter needs to be able to handle them.
This can be achieved by wrapping the CommandInterpreter
in other helper interpreters.
This allows us to do some pre/post processing on all commands before/after they
are handled.
There are three such wrappers:
The use of such wrappers will be demonstrated with the example Shutter device.
Beheading Interpreter#
Suppose that when communicating with a real shutter device, all messages are
prepended with a fixed-length header that can be ignored. For example each message
being prepended with a header of 2 bytes: \x00\x02P?
, \x00\x04T=1.0
etc.
Our simulated device needs to handle messages of the same form.
We could use regex pattern matching to avoid this header but in some cases this is
not desirable. In such cases we can cutoff the first two characters of each message
before passing it on to the CommandInterpreter
by wrapping the CommandInterpreter
with a BeheadingInterpreter
in the adapter’s __init__
as follows:
from typing import Awaitable, Callable
from tickit.adapters.interpreters.command import CommandInterpreter
from tickit.adapters.interpreters.wrappers import BeheadingInterpreter
from tickit.adapters.servers.tcp import TcpServer
from tickit.core.adapter import Adapter
class ShutterAdapter(Adapter):
def __init__(
self,
device: Shutter,
raise_interrupt: Callable[[], Awaitable[None]],
host: str = "localhost",
port: int = 25565,
) -> None:
super().__init__(
device,
raise_interrupt,
TcpServer(host, port, ByteFormat(b"%b\r\n")),
BeheadingInterpreter(CommandInterpreter(), header_size=2),
)
With the rest of the adapter unchanged. The adapter will then correctly interpret commands in messages with the header attached:
\x00\x02P?
2.0
Splitting Interpreter#
Now suppose that we would like to be able to send multiple commands in a single
message, separated by some delimiter. For example, though P?
and T?
are
interpreted as commands by the ShutterAdapter
, P? T?
is not:
P?
0.2
T?
0.2
P? T?
Request does not match any known command
This is the use-case of SplittingInterpreter
. This splits a message into multiple
sub-messages and then passes them on to another interpreter that it wraps. Wrapping
the adapter’s CommandInterprerter
as
SplittingInterpreter(CommandInterpreter(), delimiter=b" ")
with the rest of the adapter unchanged, allows for interpreting commands separated by a space:
P?
0.2
T?
0.2
P? T?
0.2
0.2
A wrapped adapter can be wrapped by another adapter wrapper. Combining the two above examples as
BeheadingInterpreter(
SplittingInterpreter(
CommandInterpreter(), delimiter=b" "
),
header_size=2
)
allows for the adapter to deal with messages with a fixed-length header containing multiple commands separated by a space:
\x00\x02P?
0.2
\x00\x05P? T?
0.2
0.2
So far we have only seen interpreter wrappers altering the received message before passing it on to another interpreter. There are also wrappers that alter the response received from the wrapped interpreter before it is sent back.
Joining Interpreter#
Above, by wrapping the ShutterAdapter
’s CommandInterpreter
with a
SplittingInterpreter
we were able to execute multiple commands from a single
message. Each executed command sent its own response, i.e. one message resulted in
multiple responses. We may instead want each message to have its own response
containing multiple command responses. This is the use-case for JoiningInterpreter
.
Wrapping the SplittingInterpreter
with this will join each of the responses into a
single message.
JoiningInterpreter(
BeheadingInterpreter(
SplittingInterpreter(
CommandInterpreter(), delimiter=b" "
),
header_size=2
),
response_delimiter=b" "
)
Results in
P?
0.2
T?
0.2
P? T?
0.2 0.2