diff --git a/application.h b/application.h index 9c06865e..4d6b2955 100644 --- a/application.h +++ b/application.h @@ -13,6 +13,9 @@ http://mozilla.org/MPL/2.0/. #include "PyInt.h" #include "network/manager.h" #include "headtrack.h" +#ifdef WITH_UART +#include "uart.h" +#endif class eu07_application { const int MAX_NETWORK_PER_FRAME = 1000; diff --git a/drivermode.cpp b/drivermode.cpp index a1b2b748..784e11a5 100644 --- a/drivermode.cpp +++ b/drivermode.cpp @@ -140,7 +140,7 @@ driver_mode::drivermode_input::command_fallback( user_command const Command ) co if( lookup == commandfallbacks.end() ) { return { user_command::none, user_command::none }; } - + return lookup->second; } @@ -600,7 +600,7 @@ driver_mode::update_camera( double const Deltatime ) { // debug camera DebugCamera.Update(); } - + // reset window state, it'll be set again if applicable in a check below Global.CabWindowOpen = false; @@ -801,7 +801,7 @@ driver_mode::OnKeyDown(int cKey) { switch (cKey) { case GLFW_KEY_F4: { - if( Global.shiftState ) { ExternalView(); } // with Shift, cycle through external views + if( Global.shiftState ) { ExternalView(); } // with Shift, cycle through external views else { InOutKey(); } // without, step out of the cab or return to it break; } @@ -826,7 +826,7 @@ driver_mode::OnKeyDown(int cKey) { } case GLFW_KEY_F6: { // przyspieszenie symulacji do testowania scenerii... uwaga na FPS! - if( DebugModeFlag ) { + if( DebugModeFlag ) { if( Global.ctrlState ) { Global.fTimeSpeed = ( Global.shiftState ? 60.0 : 20.0 ); } else { Global.fTimeSpeed = ( Global.shiftState ? 5.0 : Global.default_timespeed ); } @@ -883,7 +883,7 @@ driver_mode::DistantView( bool const Near ) { ( simulation::Train != nullptr ) ? simulation::Train->Dynamic() : pDynamicNearest ) }; - + if( vehicle == nullptr ) { return; } auto const cab = @@ -1049,7 +1049,7 @@ driver_mode::CabView() { m_externalview = false; // likwidacja obrotów - patrzy horyzontalnie na południe - Camera.Reset(); + Camera.Reset(); // bind camera with the vehicle Camera.m_owner = train->Dynamic(); diff --git a/driveruilayer.cpp b/driveruilayer.cpp index f01ed49d..c342ef13 100644 --- a/driveruilayer.cpp +++ b/driveruilayer.cpp @@ -116,7 +116,7 @@ driver_ui::on_key( int const Key, int const Action ) { } switch (Key) { - + case GLFW_KEY_TAB: { m_mappanel.is_open = !m_mappanel.is_open; @@ -130,7 +130,7 @@ driver_ui::on_key( int const Key, int const Action ) { ( m_aidpanel.is_expanded == false ) ? 1 : 2 ); state = clamp_circular( ++state, 3 ); - + m_aidpanel.is_open = ( state > 0 ); m_aidpanel.is_expanded = ( state > 1 ); @@ -144,7 +144,7 @@ driver_ui::on_key( int const Key, int const Action ) { ( m_timetablepanel.is_expanded == false ) ? 1 : 2 ); state = clamp_circular( ++state, 3 ); - + m_timetablepanel.is_open = ( state > 0 ); m_timetablepanel.is_expanded = ( state > 1 ); diff --git a/driveruipanels.cpp b/driveruipanels.cpp index 132691a7..8f76cfa4 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -34,6 +34,10 @@ http://mozilla.org/MPL/2.0/. #define DRIVER_HINT_CONTENT #include "driverhints.h" +#ifdef WITH_UART +#include "uart.h" +#endif + void drivingaid_panel::update() { @@ -548,6 +552,9 @@ debug_panel::update() { m_powergridlines.clear(); m_cameralines.clear(); m_rendererlines.clear(); +#ifdef WITH_UART + m_uartlines.clear(); +#endif update_section_vehicle( m_vehiclelines ); update_section_engine( m_enginelines ); @@ -558,6 +565,9 @@ debug_panel::update() { update_section_powergrid( m_powergridlines ); update_section_camera( m_cameralines ); update_section_renderer( m_rendererlines ); +#ifdef WITH_UART + update_section_uart(m_uartlines); +#endif } void @@ -610,6 +620,18 @@ debug_panel::render() { render_section( "Camera", m_cameralines ); render_section( "Gfx Renderer", m_rendererlines ); render_section_settings(); +#ifdef WITH_UART + if(true == render_section( "UART", m_uartlines)) { + int ports_num = UartStatus.available_ports.size(); + char **avlports = new char*[ports_num]; + for (int i=0; i < ports_num; i++) { + avlports[i] = (char *) UartStatus.available_ports[i].c_str(); + } + ImGui::Combo("Port", &UartStatus.selected_port_index, avlports, ports_num); + ImGui::Combo("Baud", &UartStatus.selected_baud_index, uart_baudrates_list, uart_baudrates_list_num); + ImGui::Checkbox("Enabled", &UartStatus.enabled); + } +#endif // toggles ImGui::Separator(); ImGui::Checkbox( "Debug Mode", &DebugModeFlag ); @@ -1213,6 +1235,35 @@ debug_panel::update_section_scantable( std::vector &Output ) { } } +#ifdef WITH_UART +void +debug_panel::update_section_uart( std::vector &Output ) { + uart_status *status = &UartStatus; + + Output.emplace_back( + ("Port: " + status->port_name).c_str(), + Global.UITextColor + ); + Output.emplace_back( + ("Baud: " + std::to_string(status->baud)).c_str(), + Global.UITextColor + ); + if(status->is_connected) { + std::string synctext = status->is_synced ? "SYNCED" : "NOT SYNCED"; + Output.emplace_back(("CONNECTED, " + synctext).c_str(), Global.UITextColor); + } else { + Output.emplace_back("* NOT CONNECTED *", Global.UITextColor); + } + Output.emplace_back( + ( + "Packets sent: "+std::to_string(status->packets_sent) + +" Packets received: "+std::to_string(status->packets_received) + ).c_str(), + Global.UITextColor + ); +} +#endif + void debug_panel::update_section_scenario( std::vector &Output ) { diff --git a/driveruipanels.h b/driveruipanels.h index c2e3e7fb..e1a9acbe 100644 --- a/driveruipanels.h +++ b/driveruipanels.h @@ -89,6 +89,9 @@ private: void update_section_powergrid( std::vector &Output ); void update_section_camera( std::vector &Output ); void update_section_renderer( std::vector &Output ); +#ifdef WITH_UART + void update_section_uart( std::vector &Output ); +#endif // section update helpers std::string update_vehicle_coupler( int const Side ); std::string update_vehicle_brake() const; @@ -97,6 +100,9 @@ private: bool render_section( std::vector const &Lines ); bool render_section_scenario(); bool render_section_eventqueue(); +#ifdef WITH_UART + bool render_section_uart(); +#endif bool render_section_settings(); // members std::array m_buffer; @@ -111,7 +117,8 @@ private: m_scenariolines, m_eventqueuelines, m_powergridlines, - m_rendererlines; + m_rendererlines, + m_uartlines; double last_time = std::numeric_limits::quiet_NaN(); diff --git a/uart.cpp b/uart.cpp index 81a87bce..77849018 100644 --- a/uart.cpp +++ b/uart.cpp @@ -7,33 +7,121 @@ #include "parser.h" #include "Logs.h" #include "simulationtime.h" +#include "application.h" + +const char* uart_baudrates_list[] = { + "300", + "1200", + "2400", + "4800", + "9600", + "19200", + "38400", + "57600", + "74880", + "115200", + "230400", + "250000", + "500000", + "1000000", + "2000000" +}; + +const size_t uart_baudrates_list_num = ( + sizeof(uart_baudrates_list)/sizeof(uart_baudrates_list[0]) + ); + +void uart_status::reset_stats() { + packets_sent = 0; + packets_received = 0; +} + +uart_status UartStatus; uart_input::uart_input() { conf = Global.uart_conf; + uart_status *status = &UartStatus; - if (!setup_port()) - throw std::runtime_error("uart: cannot open port"); + status->enabled = conf.enable; + status->port_name = conf.port; + status->baud = conf.baud; + + const std::string baudStr = std::to_string(status->baud); + + for(int i=0;iselected_baud_index = i; + status->active_port_index = i; + break; + } + } old_packet.fill(0); last_update = std::chrono::high_resolution_clock::now(); + last_setup = std::chrono::high_resolution_clock::now(); + + find_ports(); +} + +void uart_input::find_ports() { + uart_status *status = &UartStatus; + + struct sp_port **ports; + if (sp_list_ports(&ports) == SP_OK) { + status->available_ports.clear(); + status->active_port_index = -1; + status->selected_port_index = -1; + for (int i=0; ports[i]; i++) { + std::string newport = std::string(sp_get_port_name(ports[i])); + status->available_ports.emplace_back(newport); + if(newport == status->port_name) { + status->active_port_index = i; + status->selected_port_index = i; + } + } + if(status->selected_port_index > status->available_ports.size()) { + status->selected_port_index = -1; + } + sp_free_port_list(ports); + } else { + WriteLog("uart: cannot read serial ports list"); + } + last_port_find = std::chrono::high_resolution_clock::now(); } bool uart_input::setup_port() { + uart_status *status = &UartStatus; + + if(!port) { + find_ports(); + } if (port) { sp_close(port); sp_free_port(port); port = nullptr; } - if (sp_get_port_by_name(conf.port.c_str(), &port) != SP_OK) { - ErrorLog("uart: cannot find specified port"); + last_setup = std::chrono::high_resolution_clock::now(); + + if (sp_get_port_by_name(status->port_name.c_str(), &port) != SP_OK) { + if(!error_notified) { + status->is_connected = false; + ErrorLog("uart: cannot find specified port '"+conf.port+"'"); + find_ports(); + } + error_notified = true; return false; } if (sp_open(port, (sp_mode)(SP_MODE_READ | SP_MODE_WRITE)) != SP_OK) { - ErrorLog("uart: cannot open port"); + if(!error_notified) { + status->is_connected = false; + ErrorLog("uart: cannot open port '"+status->port_name+"'"); + find_ports(); + } + error_notified = true; port = nullptr; return false; } @@ -41,25 +129,43 @@ bool uart_input::setup_port() sp_port_config *config; if (sp_new_config(&config) != SP_OK || - sp_set_config_baudrate(config, conf.baud) != SP_OK || + sp_set_config_baudrate(config, status->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) { - ErrorLog("uart: cannot set config"); + if(!error_notified) { + status->is_connected = false; + ErrorLog("uart: cannot set config"); + } + error_notified = true; port = nullptr; + find_ports(); return false; } sp_free_config(config); if (sp_flush(port, SP_BUF_BOTH) != SP_OK) { - ErrorLog("uart: cannot flush"); + if(!error_notified) { + status->is_connected = false; + ErrorLog("uart: cannot flush"); + } + error_notified = true; port = nullptr; + find_ports(); return false; } - + + if(error_notified || ! status->is_connected) { + error_notified = false; + ErrorLog("uart: connected to '"+status->port_name+"'"); + status->reset_stats(); + status->is_connected = true; + data_pending = false; + } + return true; } @@ -158,11 +264,55 @@ uart_input::recall_bindings() { void uart_input::poll() { + uart_status *status = &UartStatus; auto now = std::chrono::high_resolution_clock::now(); + + /* handle baud change */ + if(status->active_baud_index != status->selected_baud_index) { + status->baud = std::stoul(uart_baudrates_list[status->selected_baud_index]); + status->active_baud_index = status->selected_baud_index; + status->reset_stats(); + status->is_connected = false; + setup_port(); + } + + /* handle port change */ + if(status->available_ports.size() > 0 && status->selected_port_index >= 0 && status->active_port_index != status->selected_port_index) { + status->port_name = status->available_ports[status->selected_port_index]; + status->active_port_index = status->selected_port_index; + status->reset_stats(); + status->is_connected = false; + setup_port(); + } + + if ( + (!port && std::chrono::duration(now - last_port_find).count() > 1.0) + || (port && std::chrono::duration(now - last_port_find).count() > 5.0) + ) { + find_ports(); + } + + if(!status->enabled) { + if(port) { + sp_close(port); + sp_free_port(port); + port = nullptr; + } + status->is_connected = false; + return; + } + if (std::chrono::duration(now - last_update).count() < conf.updatetime) return; last_update = now; + + /* if connection error occured, slow down reconnection tries */ + if (!port && error_notified && std::chrono::duration(now - last_setup).count() < 1.0) { + return; + } + + if (!port) { setup_port(); return; @@ -173,29 +323,31 @@ void uart_input::poll() return; sp_return ret; - + if ((ret = sp_input_waiting(port)) >= 20) { std::array tmp_buffer; // TBD, TODO: replace with vector of configurable size? ret = sp_blocking_read(port, (void*)tmp_buffer.data(), tmp_buffer.size(), 0); - + if (ret < 0) { setup_port(); return; } - + bool sync; if (tmp_buffer[0] != 0xEF || tmp_buffer[1] != 0xEF || tmp_buffer[2] != 0xEF || tmp_buffer[3] != 0xEF) { + UartStatus.is_synced = false; if (conf.debug) WriteLog("uart: bad sync"); sync = false; } else { + UartStatus.is_synced = true; if (conf.debug) WriteLog("uart: sync ok"); sync = true; } - + if (!sync) { int sync_cnt = 0; int sync_fail = 0; @@ -231,9 +383,10 @@ void uart_input::poll() } return; } - + std::array buffer; memmove(&buffer[0], &tmp_buffer[4], 16); + UartStatus.packets_received++; if (conf.debug) { @@ -456,7 +609,12 @@ void uart_input::poll() setup_port(); return; } + UartStatus.packets_sent++; data_pending = true; } } + +bool uart_input::is_connected() { + return (port != nullptr); +} diff --git a/uart.h b/uart.h index 16437082..42a16c11 100644 --- a/uart.h +++ b/uart.h @@ -3,6 +3,27 @@ #include #include "command.h" +extern const char* uart_baudrates_list[]; +extern const size_t uart_baudrates_list_num; + +class uart_status { + public: + std::string port_name = ""; + std::vector available_ports = {}; + int selected_port_index = -1; + int selected_baud_index = -1; + int active_port_index = -1; + int active_baud_index = -1; + int baud = 0; + bool enabled = false; + bool is_connected = false; + bool is_synced = false; + unsigned long packets_sent = 0; + unsigned long packets_received = 0; + + void reset_stats(); +}; + class uart_input { public: @@ -52,6 +73,8 @@ public: recall_bindings(); void poll(); + bool + is_connected(); private: // types @@ -64,8 +87,9 @@ private: using input_pin_t = std::tuple; using inputpin_sequence = std::vector; - + bool setup_port(); + void find_ports(); // members sp_port *port = nullptr; @@ -73,7 +97,12 @@ private: command_relay relay; std::array old_packet; // TBD, TODO: replace with vector of configurable size? std::chrono::time_point last_update; + std::chrono::time_point last_setup; + std::chrono::time_point last_port_find; conf_t conf; bool data_pending = false; + bool error_notified = false; std::uint8_t m_trainstatecab { 0 }; // helper, keeps track of last active cab. 0: front cab, 1: rear cab }; + +extern uart_status UartStatus;