PjonHL is a highlevel wrapper around PJON.
It provides a thread-aware and thread-safe C++ (>=14) interface.
One of the use-cases of PjonHL is to allow easy implementation of linux based PJON gateways or Graphical User Interfaces for example on RapsberryPi.
The target platforms for PjonHL are systems which provide
- Threading
- No constrained resources (Memory)
This document intends to give a simplified overview. For detailed documentation
please have a look into the corresponding .hpp
header files. A good start is
PjonHlBus.hpp and Connection.hpp.
PjonHL needs to be built and linked to your project.
The library comes with CMake build configuration (see CMakeLists.txt).
You may use this (e.g. via
FetchContent)
and link against the PjonHL
target in your projects.
To build the example and tests, create a build folder, run cmake and then build:
> mkdir build
> cd build
> cmake ..
> make
If the CMake target libPjon
is not defined (by your project), CMake will
automatically download Pjon as a dependency during build configuration for you.
Unit tests are built if CMake is configured to do so (enable_testing()
or
pass -DBUILD_TESTS=ON
to cmake).
This uses Catch2 as a unit test framework,
which will also be automatically downloaded during build configuration.
To run the unit tests, execute ctest
in the build folder.
If you do not use CMake, you still can build the project, you merely need to
compile the *.cpp
files in the PjonHL directory and link against them. When
compiling you need to have the Pjon source directory as an include path.
PjonHL consists of two main classes:
Represents a single PJON bus and internally handles the PJON event-loop (receive + update) in a thread. To create a Bus, you need to pass a PJON strategy:
ThroughSerial serialStrategy;
// [...] initialize strategy
// Construct a PjonHL bus:
PjonHL::Bus<ThroughSerial> bus("0.0.0.0/42", serialStrategy);
The Bus then provides methods to create "Connections":
ConnectionHandle connection = hlBus.createConnection(PjonHL::Address("0.0.0.0/53"));
The Bus
instance is then responsible for feeding the associated Connections
with data, as well as transmitting data sent over a Connection.
NOTE: A Bus instance is not to be used to directly send/receive packets. Connections should be used.
A Connection
is created by a bus and accessed through a ConnectionHandle.
It holds two Addresses:
- Address of a "remote" bus participant (or a broadcast address)
- Address of the "local" bus participant (or a broadcast address)
It allows sending and receiving of packets to a remote counterpart:
std::vector<uint8_t> data;
std::future<bool> txSuccess = connection->send(std::move(data), 1000);
Expect< std::vector<uint8_t> > received = connection->receive(1000);
As well as checking if sending or receiving succeeded:
if(txSuccess.get())
{
// [...] handle success/failure
}
if(received.isValid())
{
// [...] handle received packet
}
In addition to the two main classes the following useful helper classes are introduced:
Represents a PJON Address and provides conversion to and from a convenient string representations.
E.g. "42" or "42:3456" or full form: "0.0.0.0/42:3456"
PjonHL::Address addr("0.0.0.0/42:3456");
std::cout << "Address is " << addr.to_string();
Represents an expected/optional value. It provides a method to check if expected value is present.
In PjonHL Expect< std::vector<uint8_t> >
is used as a return type for
receive() calls, which may or may not receive data.
Expect< std::vector<uint8_t> > receivedPacket = connection.receive(1000);
if(receivedPacket.isValid())
{
std::vector<uint8_t> packet = receivedPacket.unwrap();
}
---------------------- ---------------------------------------------------
| Connection | | Bus |
---------------------- ---------------------------------------------------
| - send(vector) |------| - Bus (localAddr, Strategy) |
| - Expect receive() |* 1| - ConnectionHandle createConnection (remoteAddr)|
---------------------- ---------------------------------------------------
Pseudo code below shows the general Idea. Full example see here.
ThroughSerial serialStrategy;
// [...] initialize strategy
// Construct a PjonHL bus:
PjonHL::Bus<ThroughSerial> bus("0.0.0.0/42", serialStrategy);
// Create a Connection on the bus:
ConnectionHandle connection = hlBus.createConnection(PjonHL::Address("0.0.0.0/53"));
std::vector<uint8_t> data;
// [...] fill data vector
// Send a packet to the remote side using the conneciton:
std::future<bool> success = connection->send(std::move(data), 1000);
// [...] Now could do some other processing in parallel like sending some other
// packets, as send() does not block.
// Now wait and check if send succeeded:
if(success.get())
{
// [...] handle success/failure
}
// Try to receive data from the connection:
Expect< std::vector<uint8_t> > received = connection->receive(1000);
if(received.isValid())
{
// [...] handle received packet
}