mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 08:39:19 +02:00
hardware protocol v2.1: device log, diagnostic routines, manual links
Three additions, all driven from the simulator debug panel. DEVICE_LOG lets a controller send text lines; they reach the simulator log and a per connection view in the panel which keeps the last 64 of them. Diagnostic routines: a controller lists the procedures it offers, the operator picks one from a drop down and starts it, and the controller reports progress and the result. While a routine runs the controller can ask the simulator to show a message box with its own text and buttons and gets back which one was pressed, which is what a valve or gauge calibration needs. The message box appears whether the debug panel is open or not. Links can now be disconnected and reconnected by hand: disconnecting closes the serial port so the controller can be reprogrammed, and the session is rebuilt from scratch on reconnect. Ports which appear while the simulation runs can be picked from a refreshed list, given a baud rate and added as a new controller, so the manager now starts even with no uart2 entries in eu07.ini.
This commit is contained in:
@@ -94,10 +94,8 @@ bool driver_mode::drivermode_input::init()
|
||||
}
|
||||
#endif
|
||||
#ifdef WITH_HARDWARE_PROTOCOL_V2
|
||||
if (true == Global.hardware_conf.enable)
|
||||
{
|
||||
hardware = std::make_unique<hardware::hardware_manager>(Global.hardware_conf);
|
||||
}
|
||||
// started even with no links configured, so that a controller can be added from the debug panel
|
||||
hardware = std::make_unique<hardware::hardware_manager>(Global.hardware_conf);
|
||||
#endif
|
||||
#ifdef WITH_ZMQ
|
||||
if (!Global.zmq_address.empty())
|
||||
|
||||
@@ -571,6 +571,10 @@ void debug_panel::update()
|
||||
|
||||
void
|
||||
debug_panel::render() {
|
||||
#ifdef WITH_HARDWARE_PROTOCOL_V2
|
||||
// a controller waiting for an answer must be visible whether the panel is open or not
|
||||
render_hardware_prompt();
|
||||
#endif
|
||||
if( false == is_open ) { return; }
|
||||
|
||||
ImGui::PushFont(ui_layer::font_mono);
|
||||
@@ -632,15 +636,8 @@ debug_panel::render() {
|
||||
}
|
||||
#endif
|
||||
#ifdef WITH_HARDWARE_PROTOCOL_V2
|
||||
if( true == render_section( "Hardware Protocol v2", m_hardwarelines ) ) {
|
||||
bool logmessages = hardware::debug_flags.log_messages;
|
||||
if( ImGui::Checkbox( "Log protocol messages", &logmessages ) ) {
|
||||
hardware::debug_flags.log_messages = logmessages;
|
||||
}
|
||||
bool logframes = hardware::debug_flags.log_frames;
|
||||
if( ImGui::Checkbox( "Log every frame", &logframes ) ) {
|
||||
hardware::debug_flags.log_frames = logframes;
|
||||
}
|
||||
if( true == ImGui::CollapsingHeader( "Hardware Protocol v2" ) ) {
|
||||
render_section_hardware();
|
||||
}
|
||||
#endif
|
||||
// toggles
|
||||
@@ -1334,15 +1331,22 @@ debug_panel::update_section_uart( std::vector<text_line> &Output ) {
|
||||
#endif
|
||||
|
||||
#ifdef WITH_HARDWARE_PROTOCOL_V2
|
||||
|
||||
namespace {
|
||||
|
||||
char const *const hardware_baudrates[] = {
|
||||
"9600", "19200", "38400", "57600", "115200", "230400", "250000", "500000", "1000000", "2000000" };
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
debug_panel::update_section_hardware( std::vector<text_line> &Output ) {
|
||||
|
||||
auto const *manager = hardware::hardware_manager::instance();
|
||||
if( ( manager == nullptr ) || ( false == manager->active() ) ) {
|
||||
Output.emplace_back( "(no links configured, add an \"uart2 <port> <baud>\" entry to eu07.ini)", Global.UITextColor );
|
||||
return;
|
||||
}
|
||||
if( manager == nullptr ) { return; }
|
||||
|
||||
// one entry per link, in the same order as the reports; render_section_hardware()
|
||||
// interleaves them with the controls of each link
|
||||
for( auto const &report : manager->reports() ) {
|
||||
|
||||
auto const &protocol = report.protocol;
|
||||
@@ -1355,7 +1359,7 @@ debug_panel::update_section_hardware( std::vector<text_line> &Output ) {
|
||||
|
||||
textline +=
|
||||
"\n" + report.transport_kind + " " + report.endpoint
|
||||
+ " " + hardware::to_string( report.state )
|
||||
+ " " + ( report.enabled ? hardware::to_string( report.state ) : "OFF" )
|
||||
+ " / " + hardware::to_string( report.session );
|
||||
|
||||
if( report.session_id != 0 ) {
|
||||
@@ -1422,6 +1426,172 @@ debug_panel::update_section_hardware( std::vector<text_line> &Output ) {
|
||||
Output.emplace_back( textline, Global.UITextColor );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
debug_panel::render_section_hardware() {
|
||||
|
||||
auto *manager = hardware::hardware_manager::instance();
|
||||
if( manager == nullptr ) {
|
||||
ImGui::TextUnformatted( "(the hardware protocol is not running)" );
|
||||
return;
|
||||
}
|
||||
|
||||
auto const &reports = manager->reports();
|
||||
if( m_hardwarefunction.size() < reports.size() ) {
|
||||
m_hardwarefunction.resize( reports.size(), 0 );
|
||||
}
|
||||
|
||||
for( std::size_t index = 0; index < reports.size(); ++index ) {
|
||||
|
||||
auto const &report = reports[ index ];
|
||||
ImGui::PushID( static_cast<int>( index ) );
|
||||
ImGui::Separator();
|
||||
|
||||
if( index < m_hardwarelines.size() ) {
|
||||
auto const &line = m_hardwarelines[ index ];
|
||||
ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), "%s", line.data.c_str() );
|
||||
}
|
||||
|
||||
// switching a link off closes its port, so the controller can be reprogrammed
|
||||
if( ImGui::Button( report.enabled ? "Disconnect" : "Connect" ) ) {
|
||||
manager->set_link_enabled( index, false == report.enabled );
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if( ImGui::Button( "Remove" ) ) {
|
||||
manager->remove_link( index );
|
||||
ImGui::PopID();
|
||||
break;
|
||||
}
|
||||
|
||||
if( false == report.functions.empty() ) {
|
||||
|
||||
auto &selection = m_hardwarefunction[ index ];
|
||||
if( selection >= static_cast<int>( report.functions.size() ) ) { selection = 0; }
|
||||
if( selection < 0 ) { selection = 0; }
|
||||
|
||||
std::vector<char const *> names;
|
||||
names.reserve( report.functions.size() );
|
||||
for( auto const &function : report.functions ) {
|
||||
names.emplace_back( function.name.c_str() );
|
||||
}
|
||||
|
||||
bool const running = ( report.diagnostic.state == hardware::diagnostic_state::running );
|
||||
|
||||
ImGui::SetNextItemWidth( 260 * Global.ui_scale );
|
||||
ImGui::Combo( "###diagnosticfunction", &selection, names.data(), static_cast<int>( names.size() ) );
|
||||
ImGui::SameLine();
|
||||
if( true == running ) {
|
||||
if( ImGui::Button( "Cancel" ) ) {
|
||||
manager->cancel_diagnostic( index );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( ImGui::Button( "Run diagnostic" ) ) {
|
||||
manager->start_diagnostic( index, report.functions[ selection ].id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( report.diagnostic.state != hardware::diagnostic_state::idle ) {
|
||||
ImGui::Text(
|
||||
"diagnostic %u: %s %u%% %s",
|
||||
static_cast<unsigned>( report.diagnostic.function ),
|
||||
hardware::to_string( report.diagnostic.state ),
|
||||
static_cast<unsigned>( report.diagnostic.progress ),
|
||||
report.diagnostic.message.c_str() );
|
||||
}
|
||||
|
||||
if( false == report.log.empty() ) {
|
||||
if( true == ImGui::TreeNode( "Device log" ) ) {
|
||||
ImGui::BeginChild( "###devicelog", ImVec2S( 0, 140 ), true );
|
||||
for( auto const &entry : report.log ) {
|
||||
ImGui::Text(
|
||||
"[%8.3f] %-7s %s",
|
||||
entry.device_uptime_ms / 1000.0,
|
||||
hardware::to_string( entry.severity ),
|
||||
entry.text.c_str() );
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
// adding a controller which appeared after the simulation started
|
||||
ImGui::Separator();
|
||||
m_hardwareportnames = manager->available_ports();
|
||||
m_hardwareports.clear();
|
||||
for( auto const &name : m_hardwareportnames ) {
|
||||
m_hardwareports.emplace_back( name.c_str() );
|
||||
}
|
||||
|
||||
if( true == m_hardwareports.empty() ) {
|
||||
ImGui::TextUnformatted( "(no serial ports detected)" );
|
||||
}
|
||||
else {
|
||||
if( m_hardwareport >= static_cast<int>( m_hardwareports.size() ) ) { m_hardwareport = -1; }
|
||||
ImGui::SetNextItemWidth( 140 * Global.ui_scale );
|
||||
ImGui::Combo( "###hardwareport", &m_hardwareport, m_hardwareports.data(), static_cast<int>( m_hardwareports.size() ) );
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth( 120 * Global.ui_scale );
|
||||
ImGui::Combo( "###hardwarebaud", &m_hardwarebaud, hardware_baudrates, static_cast<int>( sizeof( hardware_baudrates ) / sizeof( hardware_baudrates[ 0 ] ) ) );
|
||||
ImGui::SameLine();
|
||||
if( ImGui::Button( "Add controller" ) ) {
|
||||
if( ( m_hardwareport >= 0 ) && ( m_hardwareport < static_cast<int>( m_hardwareportnames.size() ) ) ) {
|
||||
manager->add_serial_link( m_hardwareportnames[ m_hardwareport ], std::atoi( hardware_baudrates[ m_hardwarebaud ] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
bool logmessages = hardware::debug_flags.log_messages;
|
||||
if( ImGui::Checkbox( "Log protocol messages", &logmessages ) ) {
|
||||
hardware::debug_flags.log_messages = logmessages;
|
||||
}
|
||||
bool logframes = hardware::debug_flags.log_frames;
|
||||
if( ImGui::Checkbox( "Log every frame", &logframes ) ) {
|
||||
hardware::debug_flags.log_frames = logframes;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
debug_panel::render_hardware_prompt() {
|
||||
|
||||
auto *manager = hardware::hardware_manager::instance();
|
||||
if( manager == nullptr ) { return; }
|
||||
|
||||
for( auto const &report : manager->reports() ) {
|
||||
|
||||
if( false == report.prompt.active ) { continue; }
|
||||
|
||||
auto const title =
|
||||
( report.device_name.empty() ? std::string( "Controller" ) : report.device_name )
|
||||
+ "###hardwareprompt" + std::to_string( report.index );
|
||||
|
||||
bool open = true;
|
||||
ImGui::SetNextWindowSize( ImVec2S( 420, 0 ), ImGuiCond_Appearing );
|
||||
if( true == ImGui::Begin( title.c_str(), &open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize ) ) {
|
||||
ImGui::TextWrapped( "%s", report.prompt.text.c_str() );
|
||||
ImGui::Separator();
|
||||
for( std::size_t option = 0; option < report.prompt.options.size(); ++option ) {
|
||||
if( option > 0 ) { ImGui::SameLine(); }
|
||||
ImGui::PushID( static_cast<int>( option ) );
|
||||
if( ImGui::Button( report.prompt.options[ option ].c_str() ) ) {
|
||||
manager->answer_prompt( report.index, static_cast<std::uint8_t>( option ) );
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if( false == open ) {
|
||||
// closing the window without choosing tells the controller the user walked away
|
||||
manager->answer_prompt( report.index, hardware::prompt_dismissed );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
|
||||
@@ -94,6 +94,10 @@ private:
|
||||
#endif
|
||||
#ifdef WITH_HARDWARE_PROTOCOL_V2
|
||||
void update_section_hardware( std::vector<text_line> &Output );
|
||||
// interactive part of the hardware section: connect, disconnect, diagnostics
|
||||
void render_section_hardware();
|
||||
// message box a controller asked the simulator to show; rendered as its own window
|
||||
void render_hardware_prompt();
|
||||
#endif
|
||||
// section update helpers
|
||||
std::string update_vehicle_coupler( int const Side );
|
||||
@@ -122,6 +126,15 @@ private:
|
||||
m_uartlines,
|
||||
m_hardwarelines;
|
||||
|
||||
#ifdef WITH_HARDWARE_PROTOCOL_V2
|
||||
// debug panel state for the hardware protocol section
|
||||
std::vector<int> m_hardwarefunction; // selected diagnostic function, per link
|
||||
std::vector<char const *> m_hardwareports; // port names for the combo, valid for one frame
|
||||
std::vector<std::string> m_hardwareportnames;
|
||||
int m_hardwareport { -1 };
|
||||
int m_hardwarebaud { 4 }; // 115200
|
||||
#endif
|
||||
|
||||
double last_time = std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
struct graph_data
|
||||
|
||||
@@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "hardware/protocol/protocol_defs.h"
|
||||
|
||||
@@ -89,6 +90,39 @@ struct device_diagnostics
|
||||
std::string firmware_build;
|
||||
};
|
||||
|
||||
// one text line sent by the device
|
||||
struct device_log_entry
|
||||
{
|
||||
log_severity severity = log_severity::info;
|
||||
std::uint32_t device_uptime_ms = 0;
|
||||
std::string text;
|
||||
};
|
||||
|
||||
// a diagnostic routine the device offers, listed in the debug panel
|
||||
struct diagnostic_function
|
||||
{
|
||||
std::uint8_t id = 0;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
// progress of the routine currently running on the device
|
||||
struct diagnostic_report
|
||||
{
|
||||
diagnostic_state state = diagnostic_state::idle;
|
||||
std::uint8_t function = 0;
|
||||
std::uint8_t progress = 0;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
// a message box the device asked the simulator to show
|
||||
struct diagnostic_prompt
|
||||
{
|
||||
bool active = false;
|
||||
std::uint32_t id = 0;
|
||||
std::string text;
|
||||
std::vector<std::string> options;
|
||||
};
|
||||
|
||||
// everything the debug panel needs about one connection, copied out once per frame
|
||||
struct link_report
|
||||
{
|
||||
@@ -106,9 +140,17 @@ struct link_report
|
||||
std::size_t handles = 0;
|
||||
std::size_t subscriptions = 0;
|
||||
std::size_t frame_size = 0;
|
||||
// false when the link was switched off from the debug panel, which releases the port
|
||||
bool enabled = true;
|
||||
// index of the link, as the manager knows it
|
||||
std::size_t index = 0;
|
||||
transport_diagnostics transport;
|
||||
protocol_diagnostics protocol;
|
||||
device_diagnostics device;
|
||||
std::vector<diagnostic_function> functions;
|
||||
diagnostic_report diagnostic;
|
||||
diagnostic_prompt prompt;
|
||||
std::vector<device_log_entry> log;
|
||||
};
|
||||
|
||||
} // namespace hardware
|
||||
|
||||
@@ -27,6 +27,10 @@ constexpr std::size_t rx_queue_limit = 256;
|
||||
// frames waiting to be pushed out by the worker thread
|
||||
constexpr std::size_t tx_queue_limit = 512;
|
||||
constexpr std::size_t read_chunk_size = 4096;
|
||||
// how often the list of serial ports is refreshed for the debug panel
|
||||
constexpr float port_scan_interval = 2.0f;
|
||||
// the worker has nothing to do when no link is configured
|
||||
constexpr int idle_poll_interval_ms = 250;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
@@ -40,9 +44,10 @@ hardware_link::hardware_link( std::unique_ptr<hardware_transport> Transport, con
|
||||
m_decoder.set_frame_size_limit( m_config.frame_size );
|
||||
m_lastconnectattempt = std::chrono::steady_clock::now() - std::chrono::seconds( 10 );
|
||||
|
||||
m_endpoint = m_transport->endpoint();
|
||||
m_report.transport_kind = m_transport->kind();
|
||||
m_report.endpoint = m_transport->endpoint();
|
||||
m_transportstatus.endpoint = m_report.endpoint;
|
||||
m_report.endpoint = m_endpoint;
|
||||
m_transportstatus.endpoint = m_endpoint;
|
||||
}
|
||||
|
||||
hardware_link::~hardware_link()
|
||||
@@ -50,6 +55,12 @@ hardware_link::~hardware_link()
|
||||
m_transport->disconnect();
|
||||
}
|
||||
|
||||
void hardware_link::set_enabled( bool const Enabled )
|
||||
{
|
||||
m_enabled = Enabled;
|
||||
m_report.enabled = Enabled;
|
||||
}
|
||||
|
||||
void hardware_link::send_packet( packet_header const &Header, std::vector<std::uint8_t> const &Payload )
|
||||
{
|
||||
std::vector<std::uint8_t> frame;
|
||||
@@ -66,6 +77,24 @@ void hardware_link::send_packet( packet_header const &Header, std::vector<std::u
|
||||
|
||||
void hardware_link::service()
|
||||
{
|
||||
if( false == m_enabled )
|
||||
{
|
||||
// the link was switched off from the debug panel; release the port and stay quiet
|
||||
if( true == m_transport->connected() )
|
||||
{
|
||||
m_transport->disconnect();
|
||||
m_writebuffer.clear();
|
||||
m_decoder.reset();
|
||||
std::lock_guard<std::mutex> lock( m_mutex );
|
||||
m_transportstatus.connected = false;
|
||||
m_transportstatus.last_error.clear();
|
||||
m_transportdropped = true;
|
||||
m_rxqueue.clear();
|
||||
m_txqueue.clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if( false == m_transport->connected() )
|
||||
{
|
||||
auto const now = std::chrono::steady_clock::now();
|
||||
@@ -213,7 +242,9 @@ void hardware_link::update( double const Deltatime, state_snapshot const &Snapsh
|
||||
m_session.update( Deltatime, Snapshot );
|
||||
|
||||
m_report.transport = status;
|
||||
m_report.enabled = m_enabled;
|
||||
m_report.state = (
|
||||
false == m_enabled ? link_state::disconnected :
|
||||
false == status.connected ? link_state::disconnected :
|
||||
m_session.link_condition() );
|
||||
m_session.fill_report( m_report );
|
||||
@@ -221,23 +252,24 @@ void hardware_link::update( double const Deltatime, state_snapshot const &Snapsh
|
||||
|
||||
hardware_manager::hardware_manager( config const &Config ) : m_config( Config )
|
||||
{
|
||||
debug_flags.log_messages = m_config.debug;
|
||||
debug_flags.log_frames = m_config.debug_frames;
|
||||
|
||||
m_lastportscan = std::chrono::steady_clock::now() - std::chrono::seconds( 10 );
|
||||
|
||||
#ifdef WITH_UART
|
||||
for( auto const &link : m_config.serial_links )
|
||||
{
|
||||
m_links.emplace_back( std::make_unique<hardware_link>( std::make_unique<serial_transport>( link.port, link.baud ), m_config ) );
|
||||
m_links.emplace_back( std::make_shared<hardware_link>( std::make_unique<serial_transport>( link.port, link.baud ), m_config ) );
|
||||
WriteLog( "hardware: protocol v2 link on " + link.port + " @ " + std::to_string( link.baud ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
debug_flags.log_messages = m_config.debug;
|
||||
debug_flags.log_frames = m_config.debug_frames;
|
||||
|
||||
m_reports.resize( m_links.size() );
|
||||
|
||||
if( false == m_links.empty() )
|
||||
{
|
||||
m_thread = std::thread( &hardware_manager::worker, this );
|
||||
}
|
||||
// the worker also runs with no links at all, so that ports can be scanned and controllers
|
||||
// added from the debug panel while the simulation is running
|
||||
m_thread = std::thread( &hardware_manager::worker, this );
|
||||
|
||||
s_instance = this;
|
||||
}
|
||||
@@ -250,27 +282,146 @@ hardware_manager::~hardware_manager()
|
||||
{
|
||||
m_thread.join();
|
||||
}
|
||||
std::lock_guard<std::mutex> lock( m_linksmutex );
|
||||
m_links.clear();
|
||||
}
|
||||
|
||||
void hardware_manager::worker()
|
||||
{
|
||||
auto const interval = std::chrono::milliseconds( std::max( 1, m_config.poll_interval_ms ) );
|
||||
|
||||
while( false == m_quit )
|
||||
{
|
||||
for( auto &link : m_links )
|
||||
std::vector<std::shared_ptr<hardware_link>> links;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock( m_linksmutex );
|
||||
links = m_links;
|
||||
}
|
||||
|
||||
for( auto &link : links )
|
||||
{
|
||||
link->service();
|
||||
}
|
||||
|
||||
#ifdef WITH_UART
|
||||
auto const now = std::chrono::steady_clock::now();
|
||||
if( std::chrono::duration<float>( now - m_lastportscan ).count() > port_scan_interval )
|
||||
{
|
||||
m_lastportscan = now;
|
||||
auto ports = list_serial_ports();
|
||||
std::lock_guard<std::mutex> lock( m_portsmutex );
|
||||
m_ports = std::move( ports );
|
||||
}
|
||||
#endif
|
||||
|
||||
auto const interval = std::chrono::milliseconds( links.empty() ? idle_poll_interval_ms : std::max( 1, m_config.poll_interval_ms ) );
|
||||
std::this_thread::sleep_for( interval );
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<hardware_link> hardware_manager::link_at( std::size_t const Index ) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock( m_linksmutex );
|
||||
return ( Index < m_links.size() ? m_links[ Index ] : nullptr );
|
||||
}
|
||||
|
||||
std::vector<std::string> hardware_manager::available_ports() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock( m_portsmutex );
|
||||
return m_ports;
|
||||
}
|
||||
|
||||
bool hardware_manager::add_serial_link( std::string const &Port, int const Baud )
|
||||
{
|
||||
#ifdef WITH_UART
|
||||
if( true == Port.empty() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock( m_linksmutex );
|
||||
for( auto const &link : m_links )
|
||||
{
|
||||
if( link->endpoint().compare( 0, Port.size() + 1, Port + " " ) == 0 )
|
||||
{
|
||||
// the port already has a link, enabled or not
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_links.emplace_back( std::make_shared<hardware_link>( std::make_unique<serial_transport>( Port, Baud ), m_config ) );
|
||||
WriteLog( "hardware: protocol v2 link added on " + Port + " @ " + std::to_string( Baud ) );
|
||||
return true;
|
||||
#else
|
||||
( void )Port;
|
||||
( void )Baud;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool hardware_manager::remove_link( std::size_t const Index )
|
||||
{
|
||||
std::lock_guard<std::mutex> lock( m_linksmutex );
|
||||
if( Index >= m_links.size() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// the worker may still hold a reference for the rest of its pass; the port is released
|
||||
// when that reference goes away
|
||||
m_links[ Index ]->set_enabled( false );
|
||||
WriteLog( "hardware: protocol v2 link on " + m_links[ Index ]->endpoint() + " removed" );
|
||||
m_links.erase( m_links.begin() + Index );
|
||||
return true;
|
||||
}
|
||||
|
||||
void hardware_manager::set_link_enabled( std::size_t const Index, bool const Enabled )
|
||||
{
|
||||
auto const link = link_at( Index );
|
||||
if( link == nullptr )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if( link->enabled() == Enabled )
|
||||
{
|
||||
return;
|
||||
}
|
||||
link->set_enabled( Enabled );
|
||||
WriteLog( "hardware: protocol v2 link on " + link->endpoint() + ( Enabled ? " connected" : " disconnected" ) );
|
||||
}
|
||||
|
||||
bool hardware_manager::start_diagnostic( std::size_t const Index, std::uint8_t const Function )
|
||||
{
|
||||
auto const link = link_at( Index );
|
||||
return ( link != nullptr ? link->session().start_diagnostic( Function ) : false );
|
||||
}
|
||||
|
||||
void hardware_manager::cancel_diagnostic( std::size_t const Index )
|
||||
{
|
||||
auto const link = link_at( Index );
|
||||
if( link != nullptr )
|
||||
{
|
||||
link->session().cancel_diagnostic();
|
||||
}
|
||||
}
|
||||
|
||||
void hardware_manager::answer_prompt( std::size_t const Index, std::uint8_t const Option )
|
||||
{
|
||||
auto const link = link_at( Index );
|
||||
if( link != nullptr )
|
||||
{
|
||||
link->session().answer_prompt( Option );
|
||||
}
|
||||
}
|
||||
|
||||
void hardware_manager::update()
|
||||
{
|
||||
if( true == m_links.empty() )
|
||||
std::vector<std::shared_ptr<hardware_link>> links;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock( m_linksmutex );
|
||||
links = m_links;
|
||||
}
|
||||
|
||||
if( true == links.empty() )
|
||||
{
|
||||
m_reports.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -286,10 +437,12 @@ void hardware_manager::update()
|
||||
|
||||
state_registry::capture( m_snapshot );
|
||||
|
||||
for( std::size_t index = 0; index < m_links.size(); ++index )
|
||||
m_reports.resize( links.size() );
|
||||
for( std::size_t index = 0; index < links.size(); ++index )
|
||||
{
|
||||
m_links[ index ]->update( deltatime, m_snapshot );
|
||||
m_reports[ index ] = m_links[ index ]->report();
|
||||
links[ index ]->update( deltatime, m_snapshot );
|
||||
m_reports[ index ] = links[ index ]->report();
|
||||
m_reports[ index ].index = index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ http://mozilla.org/MPL/2.0/.
|
||||
// frames with it through small locked queues, and is the only one which ever touches simulation
|
||||
// state. a device which stops responding, floods the link or disappears therefore cannot stall the
|
||||
// simulation
|
||||
//
|
||||
// links may be added, switched off and removed while the simulation runs, which is what lets the
|
||||
// debug panel release a port so that a controller can be reprogrammed and then reconnected
|
||||
|
||||
namespace hardware
|
||||
{
|
||||
@@ -46,6 +49,13 @@ public:
|
||||
void update( double const Deltatime, state_snapshot const &Snapshot );
|
||||
// simulation thread: the report gathered during the last update
|
||||
link_report const &report() const { return m_report; }
|
||||
// simulation thread: the session, for the debug panel
|
||||
protocol_session &session() { return m_session; }
|
||||
|
||||
// switching a link off closes the port and stops reconnecting, which frees the device
|
||||
void set_enabled( bool const Enabled );
|
||||
bool enabled() const { return m_enabled; }
|
||||
std::string const &endpoint() const { return m_endpoint; }
|
||||
|
||||
void send_packet( packet_header const &Header, std::vector<std::uint8_t> const &Payload ) override;
|
||||
|
||||
@@ -55,6 +65,8 @@ private:
|
||||
config m_config;
|
||||
protocol_session m_session;
|
||||
link_report m_report;
|
||||
std::string m_endpoint;
|
||||
std::atomic<bool> m_enabled { true };
|
||||
|
||||
// worker thread only
|
||||
frame_decoder m_decoder;
|
||||
@@ -86,7 +98,23 @@ public:
|
||||
void update();
|
||||
|
||||
std::vector<link_report> const &reports() const { return m_reports; }
|
||||
bool active() const { return false == m_links.empty(); }
|
||||
bool active() const { return false == m_reports.empty(); }
|
||||
|
||||
// ---- operations available from the debug panel, simulation thread only ----
|
||||
|
||||
// adds a serial link while the simulation runs; false if the port is already in use
|
||||
bool add_serial_link( std::string const &Port, int const Baud );
|
||||
// removes a link; its port is released
|
||||
bool remove_link( std::size_t const Index );
|
||||
// switching a link off releases its port without forgetting the link
|
||||
void set_link_enabled( std::size_t const Index, bool const Enabled );
|
||||
|
||||
bool start_diagnostic( std::size_t const Index, std::uint8_t const Function );
|
||||
void cancel_diagnostic( std::size_t const Index );
|
||||
void answer_prompt( std::size_t const Index, std::uint8_t const Option );
|
||||
|
||||
// serial ports present in the system, refreshed by the worker thread
|
||||
std::vector<std::string> available_ports() const;
|
||||
|
||||
// the running instance, if any; used by the debug panel
|
||||
static hardware_manager *instance() { return s_instance; }
|
||||
@@ -94,14 +122,21 @@ public:
|
||||
private:
|
||||
// methods
|
||||
void worker();
|
||||
std::shared_ptr<hardware_link> link_at( std::size_t const Index ) const;
|
||||
|
||||
// members
|
||||
static hardware_manager *s_instance;
|
||||
|
||||
config m_config;
|
||||
std::vector<std::unique_ptr<hardware_link>> m_links;
|
||||
mutable std::mutex m_linksmutex;
|
||||
std::vector<std::shared_ptr<hardware_link>> m_links;
|
||||
std::vector<link_report> m_reports;
|
||||
state_snapshot m_snapshot;
|
||||
|
||||
mutable std::mutex m_portsmutex;
|
||||
std::vector<std::string> m_ports;
|
||||
std::chrono::steady_clock::time_point m_lastportscan;
|
||||
|
||||
std::chrono::steady_clock::time_point m_lastupdate;
|
||||
bool m_updatestarted = false;
|
||||
std::thread m_thread;
|
||||
|
||||
@@ -27,6 +27,7 @@ char const *to_string( message_type const Type )
|
||||
case message_type::device_ready: return "DEVICE_READY";
|
||||
case message_type::device_info: return "DEVICE_INFO";
|
||||
case message_type::device_diagnostics: return "DEVICE_DIAGNOSTICS";
|
||||
case message_type::device_log: return "DEVICE_LOG";
|
||||
case message_type::resolve_symbols: return "RESOLVE_SYMBOLS";
|
||||
case message_type::symbols_resolved: return "SYMBOLS_RESOLVED";
|
||||
case message_type::query_symbol: return "QUERY_SYMBOL";
|
||||
@@ -44,6 +45,12 @@ char const *to_string( message_type const Type )
|
||||
case message_type::property_read: return "PROPERTY_READ";
|
||||
case message_type::property_value: return "PROPERTY_VALUE";
|
||||
case message_type::property_write: return "PROPERTY_WRITE";
|
||||
case message_type::diagnostic_functions: return "DIAGNOSTIC_FUNCTIONS";
|
||||
case message_type::diagnostic_run: return "DIAGNOSTIC_RUN";
|
||||
case message_type::diagnostic_cancel: return "DIAGNOSTIC_CANCEL";
|
||||
case message_type::diagnostic_status: return "DIAGNOSTIC_STATUS";
|
||||
case message_type::diagnostic_prompt: return "DIAGNOSTIC_PROMPT";
|
||||
case message_type::diagnostic_prompt_result: return "DIAGNOSTIC_PROMPT_RESULT";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
@@ -108,6 +115,28 @@ char const *to_string( link_state const State )
|
||||
}
|
||||
}
|
||||
|
||||
char const *to_string( log_severity const Severity )
|
||||
{
|
||||
switch( Severity )
|
||||
{
|
||||
case log_severity::debug: return "debug";
|
||||
case log_severity::info: return "info";
|
||||
case log_severity::warning: return "warning";
|
||||
default: return "error";
|
||||
}
|
||||
}
|
||||
|
||||
char const *to_string( diagnostic_state const State )
|
||||
{
|
||||
switch( State )
|
||||
{
|
||||
case diagnostic_state::idle: return "IDLE";
|
||||
case diagnostic_state::running: return "RUNNING";
|
||||
case diagnostic_state::succeeded: return "SUCCEEDED";
|
||||
default: return "FAILED";
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t value_type_size( value_type const Type )
|
||||
{
|
||||
switch( Type )
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace hardware
|
||||
|
||||
// protocol version implemented by this build
|
||||
constexpr std::uint8_t protocol_version_major = 2;
|
||||
constexpr std::uint8_t protocol_version_minor = 0;
|
||||
constexpr std::uint8_t protocol_version_minor = 1;
|
||||
|
||||
// 'M2', stored little-endian in the frame header
|
||||
constexpr std::uint16_t protocol_magic = 0x324D;
|
||||
@@ -36,6 +36,10 @@ constexpr std::size_t protocol_frame_size_max = 4096;
|
||||
constexpr std::size_t protocol_symbol_name_max = 128;
|
||||
constexpr std::size_t protocol_string_max = 128;
|
||||
constexpr std::size_t protocol_symbols_per_message_max = 64;
|
||||
constexpr std::size_t protocol_diagnostic_functions_max = 32;
|
||||
constexpr std::size_t protocol_prompt_options_max = 4;
|
||||
// device log lines kept per connection for the debug panel
|
||||
constexpr std::size_t protocol_log_history = 64;
|
||||
|
||||
// message identifiers; the numbering is part of the public protocol and must not be reordered
|
||||
enum class message_type : std::uint16_t
|
||||
@@ -56,6 +60,7 @@ enum class message_type : std::uint16_t
|
||||
|
||||
device_info = 0x0010,
|
||||
device_diagnostics = 0x0011,
|
||||
device_log = 0x0012,
|
||||
|
||||
resolve_symbols = 0x0020,
|
||||
symbols_resolved = 0x0021,
|
||||
@@ -77,7 +82,14 @@ enum class message_type : std::uint16_t
|
||||
|
||||
property_read = 0x0050,
|
||||
property_value = 0x0051,
|
||||
property_write = 0x0052
|
||||
property_write = 0x0052,
|
||||
|
||||
diagnostic_functions = 0x0060,
|
||||
diagnostic_run = 0x0061,
|
||||
diagnostic_cancel = 0x0062,
|
||||
diagnostic_status = 0x0063,
|
||||
diagnostic_prompt = 0x0064,
|
||||
diagnostic_prompt_result = 0x0065
|
||||
};
|
||||
|
||||
// header flag bits
|
||||
@@ -189,9 +201,32 @@ enum capability_flag : std::uint32_t
|
||||
capability_send_controls = 0x00000004,
|
||||
capability_diagnostics = 0x00000008,
|
||||
capability_soft_takeover = 0x00000010,
|
||||
capability_control_leases = 0x00000020
|
||||
capability_control_leases = 0x00000020,
|
||||
// the device offers diagnostic routines which can be started from the debug panel
|
||||
capability_diagnostic_functions = 0x00000040
|
||||
};
|
||||
|
||||
// severity of a text line sent by the device
|
||||
enum class log_severity : std::uint8_t
|
||||
{
|
||||
debug = 0,
|
||||
info = 1,
|
||||
warning = 2,
|
||||
error = 3
|
||||
};
|
||||
|
||||
// progress of a diagnostic routine running on the device
|
||||
enum class diagnostic_state : std::uint8_t
|
||||
{
|
||||
idle = 0,
|
||||
running = 1,
|
||||
succeeded = 2,
|
||||
failed = 3
|
||||
};
|
||||
|
||||
// answer index reported when the user closes a prompt without choosing
|
||||
constexpr std::uint8_t prompt_dismissed = 0xFF;
|
||||
|
||||
// session progress, mirrors the connection lifecycle of the specification
|
||||
enum class session_state : std::uint8_t
|
||||
{
|
||||
@@ -216,6 +251,8 @@ char const *to_string( error_code const Code );
|
||||
char const *to_string( value_quality const Quality );
|
||||
char const *to_string( session_state const State );
|
||||
char const *to_string( link_state const State );
|
||||
char const *to_string( log_severity const Severity );
|
||||
char const *to_string( diagnostic_state const State );
|
||||
|
||||
// size of a fixed-width type on the wire, 0 for variable-length ones
|
||||
std::size_t value_type_size( value_type const Type );
|
||||
|
||||
@@ -108,6 +108,7 @@ void protocol_session::reset()
|
||||
m_sequence = 0;
|
||||
m_lastreceivedsequence = 0;
|
||||
m_sequenceseen = false;
|
||||
m_outgoingtransaction = 0;
|
||||
m_framesize = std::clamp<std::size_t>( m_config.frame_size, protocol_frame_size_min, protocol_frame_size_max );
|
||||
m_capabilities = 0;
|
||||
m_peermajor = 0;
|
||||
@@ -141,6 +142,10 @@ void protocol_session::reset()
|
||||
|
||||
m_diagnostics = protocol_diagnostics {};
|
||||
m_devicediagnostics = device_diagnostics {};
|
||||
m_functions.clear();
|
||||
m_diagnostic = diagnostic_report {};
|
||||
m_prompt = diagnostic_prompt {};
|
||||
m_log.clear();
|
||||
}
|
||||
|
||||
link_state protocol_session::link_condition() const
|
||||
@@ -376,6 +381,30 @@ void protocol_session::handle_packet( decoded_packet const &Packet )
|
||||
handle_device_diagnostics( Packet );
|
||||
break;
|
||||
|
||||
case message_type::device_log:
|
||||
handle_device_log( Packet );
|
||||
break;
|
||||
|
||||
case message_type::diagnostic_functions:
|
||||
handle_diagnostic_functions( Packet );
|
||||
break;
|
||||
|
||||
case message_type::diagnostic_status:
|
||||
handle_diagnostic_status( Packet );
|
||||
break;
|
||||
|
||||
case message_type::diagnostic_prompt:
|
||||
handle_diagnostic_prompt( Packet );
|
||||
break;
|
||||
|
||||
case message_type::ack:
|
||||
handle_device_answer( Packet, true );
|
||||
break;
|
||||
|
||||
case message_type::nack:
|
||||
handle_device_answer( Packet, false );
|
||||
break;
|
||||
|
||||
case message_type::resolve_symbols:
|
||||
handle_resolve_symbols( Packet );
|
||||
break;
|
||||
@@ -906,6 +935,212 @@ error_code protocol_session::execute_command( std::size_t const SymbolIndex, com
|
||||
return error_code::none;
|
||||
}
|
||||
|
||||
void protocol_session::handle_device_log( decoded_packet const &Packet )
|
||||
{
|
||||
payload_reader reader( Packet.payload );
|
||||
auto const severity = static_cast<log_severity>( reader.read_uint8() );
|
||||
auto const uptime = reader.read_uint32();
|
||||
auto const text = reader.read_string();
|
||||
|
||||
if( ( false == reader.ok() ) || ( true == text.empty() ) )
|
||||
{
|
||||
++m_diagnostics.length_errors;
|
||||
return;
|
||||
}
|
||||
|
||||
device_log_entry entry;
|
||||
entry.severity = ( severity <= log_severity::error ? severity : log_severity::info );
|
||||
entry.device_uptime_ms = uptime;
|
||||
entry.text = text;
|
||||
m_log.emplace_back( std::move( entry ) );
|
||||
while( m_log.size() > protocol_log_history )
|
||||
{
|
||||
m_log.pop_front();
|
||||
}
|
||||
|
||||
// debug chatter stays in the panel, anything the device considers worth reporting
|
||||
// also goes to the simulator log
|
||||
if( ( severity != log_severity::debug ) || ( true == debug_flags.log_messages ) )
|
||||
{
|
||||
auto const line = "hardware: [" + m_devicename + "] " + std::string( to_string( severity ) ) + ": " + text;
|
||||
if( severity == log_severity::error )
|
||||
{
|
||||
ErrorLog( line );
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog( line );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void protocol_session::handle_diagnostic_functions( decoded_packet const &Packet )
|
||||
{
|
||||
payload_reader reader( Packet.payload );
|
||||
auto const count = reader.read_uint8();
|
||||
if( ( false == reader.ok() ) || ( count > protocol_diagnostic_functions_max ) )
|
||||
{
|
||||
send_nack( 0, message_type::diagnostic_functions, error_code::invalid_field );
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<diagnostic_function> functions;
|
||||
functions.reserve( count );
|
||||
for( std::size_t index = 0; index < count; ++index )
|
||||
{
|
||||
diagnostic_function function;
|
||||
function.id = reader.read_uint8();
|
||||
function.name = reader.read_string();
|
||||
if( false == reader.ok() )
|
||||
{
|
||||
send_nack( 0, message_type::diagnostic_functions, error_code::invalid_length );
|
||||
return;
|
||||
}
|
||||
if( false == function.name.empty() )
|
||||
{
|
||||
functions.emplace_back( std::move( function ) );
|
||||
}
|
||||
}
|
||||
|
||||
m_functions = std::move( functions );
|
||||
if( true == debug_flags.log_messages )
|
||||
{
|
||||
WriteLog( "hardware: device '" + m_devicename + "' offers " + std::to_string( m_functions.size() ) + " diagnostic function(s)" );
|
||||
}
|
||||
}
|
||||
|
||||
void protocol_session::handle_diagnostic_status( decoded_packet const &Packet )
|
||||
{
|
||||
payload_reader reader( Packet.payload );
|
||||
auto const function = reader.read_uint8();
|
||||
auto const state = static_cast<diagnostic_state>( reader.read_uint8() );
|
||||
auto const progress = reader.read_uint8();
|
||||
auto const message = reader.read_string();
|
||||
|
||||
if( ( false == reader.ok() ) || ( state > diagnostic_state::failed ) )
|
||||
{
|
||||
++m_diagnostics.length_errors;
|
||||
return;
|
||||
}
|
||||
|
||||
m_diagnostic.function = function;
|
||||
m_diagnostic.state = state;
|
||||
m_diagnostic.progress = ( progress <= 100 ? progress : 100 );
|
||||
m_diagnostic.message = message;
|
||||
|
||||
if( state != diagnostic_state::running )
|
||||
{
|
||||
// a routine which ended cannot be waiting for an answer any more
|
||||
m_prompt = diagnostic_prompt {};
|
||||
WriteLog( "hardware: device '" + m_devicename + "' diagnostic " + std::to_string( function ) + " " + std::string( to_string( state ) ) + ( message.empty() ? "" : ": " + message ) );
|
||||
}
|
||||
}
|
||||
|
||||
void protocol_session::handle_diagnostic_prompt( decoded_packet const &Packet )
|
||||
{
|
||||
payload_reader reader( Packet.payload );
|
||||
diagnostic_prompt prompt;
|
||||
prompt.id = reader.read_uint32();
|
||||
prompt.text = reader.read_string();
|
||||
auto const options = reader.read_uint8();
|
||||
|
||||
if( ( false == reader.ok() ) || ( options > protocol_prompt_options_max ) )
|
||||
{
|
||||
send_nack( 0, message_type::diagnostic_prompt, error_code::invalid_field );
|
||||
return;
|
||||
}
|
||||
|
||||
for( std::size_t index = 0; index < options; ++index )
|
||||
{
|
||||
auto label = reader.read_string();
|
||||
if( false == reader.ok() )
|
||||
{
|
||||
send_nack( 0, message_type::diagnostic_prompt, error_code::invalid_length );
|
||||
return;
|
||||
}
|
||||
prompt.options.emplace_back( std::move( label ) );
|
||||
}
|
||||
|
||||
if( true == prompt.options.empty() )
|
||||
{
|
||||
prompt.options.emplace_back( "OK" );
|
||||
}
|
||||
prompt.active = true;
|
||||
m_prompt = std::move( prompt );
|
||||
}
|
||||
|
||||
void protocol_session::handle_device_answer( decoded_packet const &Packet, bool const Positive )
|
||||
{
|
||||
payload_reader reader( Packet.payload );
|
||||
reader.read_uint32();
|
||||
auto const message = static_cast<message_type>( reader.read_uint16() );
|
||||
auto const error = ( Positive ? error_code::none : static_cast<error_code>( reader.read_uint16() ) );
|
||||
|
||||
if( false == reader.ok() )
|
||||
{
|
||||
++m_diagnostics.length_errors;
|
||||
return;
|
||||
}
|
||||
|
||||
if( ( message == message_type::diagnostic_run ) && ( false == Positive ) )
|
||||
{
|
||||
m_diagnostic.state = diagnostic_state::failed;
|
||||
m_diagnostic.message = to_string( error );
|
||||
WriteLog( "hardware: device '" + m_devicename + "' refused to start the diagnostic: " + std::string( to_string( error ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool protocol_session::start_diagnostic( std::uint8_t const Function )
|
||||
{
|
||||
if( m_state != session_state::ready )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto const function = std::find_if( m_functions.begin(), m_functions.end(), [ Function ]( diagnostic_function const &Entry ) { return Entry.id == Function; } );
|
||||
if( function == m_functions.end() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
payload_writer writer;
|
||||
writer.write_uint32( ++m_outgoingtransaction );
|
||||
writer.write_uint8( Function );
|
||||
send( message_type::diagnostic_run, packetflag_ack_required, writer );
|
||||
|
||||
m_prompt = diagnostic_prompt {};
|
||||
m_diagnostic.function = Function;
|
||||
m_diagnostic.state = diagnostic_state::running;
|
||||
m_diagnostic.progress = 0;
|
||||
m_diagnostic.message = "requested";
|
||||
WriteLog( "hardware: device '" + m_devicename + "' asked to run diagnostic '" + function->name + "'" );
|
||||
return true;
|
||||
}
|
||||
|
||||
void protocol_session::cancel_diagnostic()
|
||||
{
|
||||
if( m_state != session_state::ready )
|
||||
{
|
||||
return;
|
||||
}
|
||||
payload_writer writer;
|
||||
writer.write_uint8( m_diagnostic.function );
|
||||
send( message_type::diagnostic_cancel, packetflag_none, writer );
|
||||
m_prompt = diagnostic_prompt {};
|
||||
}
|
||||
|
||||
void protocol_session::answer_prompt( std::uint8_t const Option )
|
||||
{
|
||||
if( false == m_prompt.active )
|
||||
{
|
||||
return;
|
||||
}
|
||||
payload_writer writer;
|
||||
writer.write_uint32( m_prompt.id );
|
||||
writer.write_uint8( Option );
|
||||
send( message_type::diagnostic_prompt_result, packetflag_response, writer );
|
||||
m_prompt = diagnostic_prompt {};
|
||||
}
|
||||
|
||||
void protocol_session::send_state_update( std::vector<state_item> const &Items, bool const Snapshot )
|
||||
{
|
||||
if( true == Items.empty() )
|
||||
@@ -1052,6 +1287,10 @@ void protocol_session::fill_report( link_report &Report ) const
|
||||
Report.frame_size = m_framesize;
|
||||
Report.protocol = m_diagnostics;
|
||||
Report.device = m_devicediagnostics;
|
||||
Report.functions = m_functions;
|
||||
Report.diagnostic = m_diagnostic;
|
||||
Report.prompt = m_prompt;
|
||||
Report.log.assign( m_log.begin(), m_log.end() );
|
||||
}
|
||||
|
||||
} // namespace hardware
|
||||
|
||||
@@ -56,6 +56,16 @@ public:
|
||||
// copies the current condition into the debug report
|
||||
void fill_report( link_report &Report ) const;
|
||||
|
||||
// ---- diagnostics offered by the device, driven from the debug panel ----
|
||||
// starts one of the routines the device advertised; false when it isn't available
|
||||
bool start_diagnostic( std::uint8_t const Function );
|
||||
void cancel_diagnostic();
|
||||
// answers the message box the device asked the simulator to show
|
||||
void answer_prompt( std::uint8_t const Option );
|
||||
bool has_prompt() const { return m_prompt.active; }
|
||||
diagnostic_prompt const &prompt() const { return m_prompt; }
|
||||
std::vector<diagnostic_function> const &functions() const { return m_functions; }
|
||||
|
||||
session_state state() const { return m_state; }
|
||||
link_state link_condition() const;
|
||||
protocol_diagnostics const &diagnostics() const { return m_diagnostics; }
|
||||
@@ -84,6 +94,11 @@ private:
|
||||
void handle_control_set( decoded_packet const &Packet );
|
||||
void handle_device_diagnostics( decoded_packet const &Packet );
|
||||
void handle_heartbeat( decoded_packet const &Packet );
|
||||
void handle_device_log( decoded_packet const &Packet );
|
||||
void handle_diagnostic_functions( decoded_packet const &Packet );
|
||||
void handle_diagnostic_status( decoded_packet const &Packet );
|
||||
void handle_diagnostic_prompt( decoded_packet const &Packet );
|
||||
void handle_device_answer( decoded_packet const &Packet, bool const Positive );
|
||||
|
||||
void send_state_update( std::vector<state_item> const &Items, bool const Snapshot );
|
||||
void send_symbol_context_changed( std::uint32_t const Context );
|
||||
@@ -109,6 +124,8 @@ private:
|
||||
std::uint32_t m_sessionid = 0;
|
||||
std::uint32_t m_sequence = 0;
|
||||
std::uint32_t m_lastreceivedsequence = 0;
|
||||
// transaction numbering for the few requests the simulator itself makes
|
||||
std::uint32_t m_outgoingtransaction = 0;
|
||||
bool m_sequenceseen = false;
|
||||
std::size_t m_framesize = protocol_frame_size_default;
|
||||
std::uint32_t m_capabilities = 0;
|
||||
@@ -150,6 +167,10 @@ private:
|
||||
|
||||
protocol_diagnostics m_diagnostics;
|
||||
device_diagnostics m_devicediagnostics;
|
||||
std::vector<diagnostic_function> m_functions;
|
||||
diagnostic_report m_diagnostic;
|
||||
diagnostic_prompt m_prompt;
|
||||
std::deque<device_log_entry> m_log;
|
||||
rate_counter m_rxrate;
|
||||
rate_counter m_txrate;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user