blob: 314235ca865f09d6d969f711c87214c75cd9bc65 [file] [log] [blame]
.. _module-pw_hdlc-api:
=============
API reference
=============
.. pigweed-module-subpage::
:name: pw_hdlc
The ``pw_hdlc`` API has 3 conceptual parts:
* :ref:`module-pw_hdlc-api-encoder`: Encode data as HDLC unnumbered
information frames.
* :ref:`module-pw_hdlc-api-decoder`: Decode HDLC frames from a stream of data.
* :ref:`module-pw_hdlc-api-rpc`: Use RPC over HDLC.
.. _module-pw_hdlc-api-encoder:
-------
Encoder
-------
Single-Function Encoding
========================
Pigweed offers a single function which will encode an HDLC frame in each of
C++, Python, and TypeScript:
.. tab-set::
.. tab-item:: C++
:sync: cpp
.. doxygenfunction:: pw::hdlc::WriteUIFrame(uint64_t address, ConstByteSpan data, stream::Writer &writer)
Example:
.. code-block:: cpp
// Writes a span of data to a pw::stream::Writer and returns the status. This
// implementation uses the pw_checksum module to compute the CRC-32 frame check
// sequence.
#include "pw_hdlc/encoder.h"
#include "pw_hdlc/sys_io_stream.h"
int main() {
pw::stream::SysIoWriter serial_writer;
Status status = WriteUIFrame(123 /* address */, data, serial_writer);
if (!status.ok()) {
PW_LOG_INFO("Writing frame failed! %s", status.str());
}
}
.. tab-item:: Python
:sync: py
.. automodule:: pw_hdlc.encode
:members:
:noindex:
Example:
.. code-block:: python
# Read bytes from serial and encode HDLC frames
import serial
from pw_hdlc import encode
ser = serial.Serial()
address = 123
ser.write(encode.ui_frame(address, b'your data here!'))
.. tab-item:: TypeScript
:sync: ts
``Encoder`` provides a way to build complete, escaped HDLC unnumbered
information frames.
.. js:method:: Encoder.uiFrame(address, data)
:noindex:
:param number address: frame address.
:param Uint8Array data: frame data.
:returns: ``Uint8Array`` containing a complete HDLC frame.
Piecemeal Encoding
==================
Additionally, the C++ API provides an API for piecemeal encoding of an HDLC
frame. This allows frames to be encoded gradually without ever holding an
entire frame in memory at once.
.. doxygenclass:: pw::hdlc::Encoder
.. _module-pw_hdlc-api-decoder:
-------
Decoder
-------
.. tab-set::
.. tab-item:: C++
:sync: cpp
.. doxygenclass:: pw::hdlc::Decoder
:members:
Example:
.. code-block:: cpp
// Read individual bytes from pw::sys_io and decode HDLC frames.
#include "pw_hdlc/decoder.h"
#include "pw_sys_io/sys_io.h"
int main() {
std::byte data;
while (true) {
if (!pw::sys_io::ReadByte(&data).ok()) {
// Log serial reading error
}
Result<Frame> decoded_frame = decoder.Process(data);
if (decoded_frame.ok()) {
// Handle the decoded frame
}
}
}
.. tab-item:: Python
:sync: py
.. autoclass:: pw_hdlc.decode.FrameDecoder
:members:
:noindex:
Example:
.. code-block:: python
# Decode data read from serial
import serial
from pw_hdlc import decode
ser = serial.Serial()
decoder = decode.FrameDecoder()
while True:
for frame in decoder.process_valid_frames(ser.read()):
# Handle the decoded frame
It is possible to decode HDLC frames from a stream using different protocols or
unstructured data. This is not recommended, but may be necessary when
introducing HDLC to an existing system.
The ``FrameAndNonFrameDecoder`` Python class supports working with raw data and
HDLC frames in the same stream.
.. autoclass:: pw_hdlc.decode.FrameAndNonFrameDecoder
:members:
:noindex:
.. tab-item:: TypeScript
:sync: ts
``Decoder`` unescapes received bytes and adds them to a buffer. Complete,
valid HDLC frames are yielded as they are received.
.. js:method:: Decoder.process(data)
:noindex:
:param Uint8Array data: bytes to be decoded.
:yields: HDLC frames, including corrupt frames.
The ``Frame.ok()`` method whether the frame is valid.
.. js:method:: processValidFrames(data)
:noindex:
:param Uint8Array data: bytes to be decoded.
:yields: Valid HDLC frames, logging any errors.
.. _module-pw_hdlc-api-rpc:
---
RPC
---
.. tab-set::
.. tab-item:: C++
:sync: cpp
``RpcChannelOutput`` implements the ``pw::rpc::ChannelOutput`` interface
of ``pw_rpc``, simplifying the process of creating an RPC channel over HDLC.
A ``pw::stream::Writer`` must be provided as the underlying transport
implementation.
If your HDLC routing path has a Maximum Transmission Unit (MTU) limitation,
use the ``FixedMtuChannelOutput`` to verify that the currently configured
max RPC payload size (dictated by the static encode buffer of ``pw_rpc``)
will always fit safely within the limits of the fixed HDLC MTU *after*
HDLC encoding.
.. tab-item:: Python
:sync: py
The ``pw_hdlc`` Python package includes utilities to HDLC-encode and
decode RPC packets, with examples of RPC client implementations in Python.
It also provides abstractions for interfaces used to receive RPC Packets.
The ``pw_hdlc.rpc.CancellableReader`` and ``pw_hdlc.rpc.RpcClient``
classes and derived classes are context-managed to cleanly cancel the
read process and stop the reader thread. The ``pw_hdlc.rpc.SocketReader``
and ``pw_hdlc.rpc.SerialReader`` also close the provided interface on
context exit. It is recommended to use these in a context statement. For
example:
.. code-block:: python
import serial
from pw_hdlc import rpc
if __name__ == '__main__':
serial_device = serial.Serial('/dev/ttyACM0')
with rpc.SerialReader(serial_device) as reader:
with rpc.HdlcRpcClient(
reader,
[],
rpc.default_channels(serial_device.write)) as rpc_client:
# Do something with rpc_client.
# The serial_device object is closed, and reader thread stopped.
return 0
.. autoclass:: pw_hdlc.rpc.channel_output
:members:
:noindex:
.. autoclass:: pw_hdlc.rpc.CancellableReader
:members:
:noindex:
.. autoclass:: pw_hdlc.rpc.SelectableReader
:members:
:noindex:
.. autoclass:: pw_hdlc.rpc.SocketReader
:members:
:noindex:
.. autoclass:: pw_hdlc.rpc.SerialReader
:members:
:noindex:
.. autoclass:: pw_hdlc.rpc.DataReaderAndExecutor
:members:
:noindex:
.. autoclass:: pw_hdlc.rpc.default_channels
:members:
:noindex:
.. autoclass:: pw_hdlc.rpc.RpcClient
:members:
:noindex:
.. autoclass:: pw_hdlc.rpc.HdlcRpcClient
:members:
:noindex:
.. autoclass:: pw_hdlc.rpc.NoEncodingSingleChannelRpcClient
:members:
:noindex:
.. autoclass:: pw_hdlc.rpc.SocketSubprocess
:members:
:noindex:
.. autoclass:: pw_hdlc.rpc.HdlcRpcLocalServerAndClient
:members:
:noindex:
.. tab-item:: TypeScript
:sync: ts
The TypeScript library doesn't have an RPC interface.
-----------------
More pw_hdlc docs
-----------------
.. include:: docs.rst
:start-after: .. pw_hdlc-nav-start
:end-before: .. pw_hdlc-nav-end