mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-02 15:09:13 +01:00
hardware protocol v2: sessions, subscriptions and the transport worker
Adds the logical protocol layer on top of the frame codec: session handshake with negotiated frame size, session scoped symbol handles, state subscriptions with periodic, on change and deadband filtering, command events with duplicate protection and continuous controls with stale update rejection, plus per connection rate limits and diagnostics. A single worker thread owns all of the blocking transport work and exchanges finished frames with the simulation thread through short locked queues, so a stalled, flooding or unplugged controller cannot hold up the simulation.
This commit is contained in:
47
hardware/transport/hardware_transport.h
Normal file
47
hardware/transport/hardware_transport.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
// transport abstraction of the hardware protocol
|
||||
// the upper layers only ever see a byte stream, which is what lets the same protocol run over
|
||||
// serial, tcp or rs-485 without changing message semantics
|
||||
// every method is called from the hardware worker thread only
|
||||
|
||||
namespace hardware
|
||||
{
|
||||
|
||||
class hardware_transport
|
||||
{
|
||||
|
||||
public:
|
||||
// methods
|
||||
virtual ~hardware_transport() = default;
|
||||
|
||||
virtual bool connected() const = 0;
|
||||
// tries to establish the link, returns true on success
|
||||
virtual bool connect() = 0;
|
||||
virtual void disconnect() = 0;
|
||||
// non-blocking read, returns the number of bytes placed in the buffer or -1 on error
|
||||
virtual int read( std::uint8_t *Buffer, std::size_t const Size ) = 0;
|
||||
// non-blocking write, returns the number of bytes accepted or -1 on error
|
||||
virtual int write( std::uint8_t const *Data, std::size_t const Size ) = 0;
|
||||
|
||||
// human readable transport type, i.e. "Serial"
|
||||
virtual std::string kind() const = 0;
|
||||
// human readable endpoint, i.e. "COM4 @ 115200"
|
||||
virtual std::string endpoint() const = 0;
|
||||
// last error reported by the underlying device
|
||||
virtual std::string last_error() const = 0;
|
||||
};
|
||||
|
||||
} // namespace hardware
|
||||
151
hardware/transport/serial_transport.cpp
Normal file
151
hardware/transport/serial_transport.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "hardware/transport/serial_transport.h"
|
||||
|
||||
namespace hardware
|
||||
{
|
||||
|
||||
serial_transport::serial_transport( std::string Port, int const Baud ) : m_portname( std::move( Port ) ), m_baud( Baud )
|
||||
{
|
||||
}
|
||||
|
||||
serial_transport::~serial_transport()
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
std::string serial_transport::endpoint() const
|
||||
{
|
||||
return m_portname + " @ " + std::to_string( m_baud );
|
||||
}
|
||||
|
||||
bool serial_transport::connect()
|
||||
{
|
||||
disconnect();
|
||||
|
||||
sp_port *port = nullptr;
|
||||
if( sp_get_port_by_name( m_portname.c_str(), &port ) != SP_OK )
|
||||
{
|
||||
m_lasterror = "port not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
if( sp_open( port, static_cast<sp_mode>( SP_MODE_READ | SP_MODE_WRITE ) ) != SP_OK )
|
||||
{
|
||||
m_lasterror = "cannot open port";
|
||||
sp_free_port( port );
|
||||
return false;
|
||||
}
|
||||
|
||||
sp_port_config *config = nullptr;
|
||||
if( ( sp_new_config( &config ) != SP_OK )
|
||||
|| ( sp_set_config_baudrate( config, m_baud ) != SP_OK )
|
||||
|| ( sp_set_config_flowcontrol( config, SP_FLOWCONTROL_NONE ) != SP_OK )
|
||||
|| ( sp_set_config_bits( config, 8 ) != SP_OK )
|
||||
|| ( sp_set_config_stopbits( config, 1 ) != SP_OK )
|
||||
|| ( sp_set_config_parity( config, SP_PARITY_NONE ) != SP_OK )
|
||||
|| ( sp_set_config( port, config ) != SP_OK ) )
|
||||
{
|
||||
m_lasterror = "cannot configure port";
|
||||
if( config != nullptr )
|
||||
{
|
||||
sp_free_config( config );
|
||||
}
|
||||
sp_close( port );
|
||||
sp_free_port( port );
|
||||
return false;
|
||||
}
|
||||
sp_free_config( config );
|
||||
|
||||
if( sp_flush( port, SP_BUF_BOTH ) != SP_OK )
|
||||
{
|
||||
m_lasterror = "cannot flush port";
|
||||
sp_close( port );
|
||||
sp_free_port( port );
|
||||
return false;
|
||||
}
|
||||
|
||||
m_port = port;
|
||||
m_lasterror.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void serial_transport::disconnect()
|
||||
{
|
||||
if( m_port == nullptr )
|
||||
{
|
||||
return;
|
||||
}
|
||||
sp_close( m_port );
|
||||
sp_free_port( m_port );
|
||||
m_port = nullptr;
|
||||
}
|
||||
|
||||
int serial_transport::read( std::uint8_t *Buffer, std::size_t const Size )
|
||||
{
|
||||
if( m_port == nullptr )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int const waiting = sp_input_waiting( m_port );
|
||||
if( waiting < 0 )
|
||||
{
|
||||
m_lasterror = "read failed";
|
||||
return -1;
|
||||
}
|
||||
if( waiting == 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int const result = sp_nonblocking_read( m_port, Buffer, std::min( Size, static_cast<std::size_t>( waiting ) ) );
|
||||
if( result < 0 )
|
||||
{
|
||||
m_lasterror = "read failed";
|
||||
return -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int serial_transport::write( std::uint8_t const *Data, std::size_t const Size )
|
||||
{
|
||||
if( m_port == nullptr )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int const result = sp_nonblocking_write( m_port, Data, Size );
|
||||
if( result < 0 )
|
||||
{
|
||||
m_lasterror = "write failed";
|
||||
return -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> list_serial_ports()
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
sp_port **ports = nullptr;
|
||||
if( sp_list_ports( &ports ) != SP_OK )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
for( int index = 0; ports[ index ] != nullptr; ++index )
|
||||
{
|
||||
result.emplace_back( sp_get_port_name( ports[ index ] ) );
|
||||
}
|
||||
sp_free_port_list( ports );
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace hardware
|
||||
54
hardware/transport/serial_transport.h
Normal file
54
hardware/transport/serial_transport.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libserialport.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "hardware/transport/hardware_transport.h"
|
||||
|
||||
// serial port transport, one connection per port, as described by the specification
|
||||
// the port is opened and serviced exclusively from the hardware worker thread
|
||||
|
||||
namespace hardware
|
||||
{
|
||||
|
||||
class serial_transport : public hardware_transport
|
||||
{
|
||||
|
||||
public:
|
||||
// methods
|
||||
serial_transport( std::string Port, int const Baud );
|
||||
~serial_transport() override;
|
||||
|
||||
bool connected() const override { return m_port != nullptr; }
|
||||
bool connect() override;
|
||||
void disconnect() override;
|
||||
int read( std::uint8_t *Buffer, std::size_t const Size ) override;
|
||||
int write( std::uint8_t const *Data, std::size_t const Size ) override;
|
||||
|
||||
std::string kind() const override { return "Serial"; }
|
||||
std::string endpoint() const override;
|
||||
std::string last_error() const override { return m_lasterror; }
|
||||
|
||||
private:
|
||||
// members
|
||||
std::string m_portname;
|
||||
int m_baud = 115200;
|
||||
sp_port *m_port = nullptr;
|
||||
std::string m_lasterror;
|
||||
};
|
||||
|
||||
// list of serial ports currently present in the system
|
||||
std::vector<std::string> list_serial_ports();
|
||||
|
||||
} // namespace hardware
|
||||
Reference in New Issue
Block a user