diff --git a/OVMS.V3/components/can/src/canlog_tcpserver.h b/OVMS.V3/components/can/src/canlog_tcpserver.h index dace3b4..fd1178e 100644 --- a/OVMS.V3/components/can/src/canlog_tcpserver.h +++ b/OVMS.V3/components/can/src/canlog_tcpserver.h @@ -32,7 +32,6 @@ #ifdef CONFIG_OVMS_SC_GPL_MONGOOSE #include "canlog.h" -#include "canlog_tcpserver.h" #include "ovms_netmanager.h" #include "ovms_mutex.h" diff --git a/OVMS.V3/components/can/src/canlog_udpserver.cpp b/OVMS.V3/components/can/src/canlog_udpserver.cpp new file mode 100644 index 0000000..9891805 --- /dev/null +++ b/OVMS.V3/components/can/src/canlog_udpserver.cpp @@ -0,0 +1,330 @@ +/* +; Project: Open Vehicle Monitor System +; Module: CAN logging framework +; Date: 18th January 2018 +; +; (C) 2018 Michael Balzer +; +; Permission is hereby granted, free of charge, to any person obtaining a copy +; of this software and associated documentation files (the "Software"), to deal +; in the Software without restriction, including without limitation the rights +; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +; copies of the Software, and to permit persons to whom the Software is +; furnished to do so, subject to the following conditions: +; +; The above copyright notice and this permission notice shall be included in +; all copies or substantial portions of the Software. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +; THE SOFTWARE. +*/ + +#include +#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE + +#include "ovms_log.h" +static const char *TAG = "canlog-udpserver"; + +#include "can.h" +#include "canformat.h" +#include "canlog_udpserver.h" +#include "ovms_config.h" +#include "ovms_events.h" +#include "ovms_peripherals.h" + +#define UDP_TIMEOUT 30 + +canlog_udpserver* MyCanLogUdpServer = NULL; + +udpcanlogconnection::udpcanlogconnection(canlog* logger, std::string format, canformat::canformat_serve_mode_t mode) + : canlogconnection(logger, format, mode) + { + m_timeout = monotonictime + UDP_TIMEOUT; + } + +udpcanlogconnection::~udpcanlogconnection() + { + } + +void udpcanlogconnection::OutputMsg(CAN_log_message_t& msg, std::string &result) + { + m_msgcount++; + + if ((m_filters != NULL) && (! m_filters->IsFiltered(&msg.frame))) + { + m_filtercount++; + return; + } + + if (result.length()>0) + { + sendto(m_sock, (const char*)result.c_str(), result.length(), 0, &m_sa, sizeof(m_sa)); + } + } + +void udpcanlogconnection::Tickle() + { + m_timeout = monotonictime + UDP_TIMEOUT; + } + +void can_log_udpserver_start(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv) + { + std::string format(cmd->GetName()); + std::string mode(cmd->GetParent()->GetName()); + canlog_udpserver* logger = new canlog_udpserver(argv[0],format,GetFormatModeType(mode)); + + if (logger->Open()) + { + if (argc>1) + { MyCan.AddLogger(logger, argc-1, &argv[1]); } + else + { MyCan.AddLogger(logger); } + writer->printf("CAN logging as UDP server: %s\n", logger->GetInfo().c_str()); + } + else + { + writer->printf("Error: Could not start CAN logging as UDP server: %s\n",logger->GetInfo().c_str()); + delete logger; + } + } + +class OvmsCanLogUdpServerInit + { + public: + OvmsCanLogUdpServerInit(); + void NetManInit(std::string event, void* data); + void NetManStop(std::string event, void* data); + } MyOvmsCanLogUdpServerInit __attribute__ ((init_priority (4560))); + +OvmsCanLogUdpServerInit::OvmsCanLogUdpServerInit() + { + ESP_LOGI(TAG, "Initialising CAN logging as UDP server (4560)"); + + OvmsCommand* cmd_can = MyCommandApp.FindCommand("can"); + if (cmd_can) + { + OvmsCommand* cmd_can_log = cmd_can->FindCommand("log"); + if (cmd_can_log) + { + OvmsCommand* cmd_can_log_start = cmd_can_log->FindCommand("start"); + if (cmd_can_log_start) + { + // We have a place to put our command tree.. + OvmsCommand* start = cmd_can_log_start->RegisterCommand("udpserver", "CAN logging as UDP server"); + OvmsCommand* discard = start->RegisterCommand("discard","CAN logging as UDP server (discard mode)"); + OvmsCommand* simulate = start->RegisterCommand("simulate","CAN logging as UDP server (simulate mode)"); + OvmsCommand* transmit = start->RegisterCommand("transmit","CAN logging as UDP server (transmit mode)"); + MyCanFormatFactory.RegisterCommandSet(discard, "Start CAN logging as UDP server (discard mode)", + can_log_udpserver_start, + " [filter1] ... [filterN]\n" + "Filter: | [-] | :[-]\n" + "Example: 2:2a0-37f", + 1, 9); + MyCanFormatFactory.RegisterCommandSet(simulate, "Start CAN logging as UDP server (simulate mode)", + can_log_udpserver_start, + " [filter1] ... [filterN]\n" + "Filter: | [-] | :[-]\n" + "Example: 2:2a0-37f", + 1, 9); + MyCanFormatFactory.RegisterCommandSet(transmit, "Start CAN logging as UDP server (transmit mode)", + can_log_udpserver_start, + " [filter1] ... [filterN]\n" + "Filter: | [-] | :[-]\n" + "Example: 2:2a0-37f", + 1, 9); + } + } + } + + using std::placeholders::_1; + using std::placeholders::_2; + MyEvents.RegisterEvent(TAG, "network.mgr.init", std::bind(&OvmsCanLogUdpServerInit::NetManInit, this, _1, _2)); + MyEvents.RegisterEvent(TAG, "network.mgr.stop", std::bind(&OvmsCanLogUdpServerInit::NetManStop, this, _1, _2)); + } + +void OvmsCanLogUdpServerInit::NetManInit(std::string event, void* data) + { + if (MyCanLogUdpServer) MyCanLogUdpServer->Open(); + } + +void OvmsCanLogUdpServerInit::NetManStop(std::string event, void* data) + { + if (MyCanLogUdpServer) MyCanLogUdpServer->Close(); + } + +static void tsMongooseHandler(struct mg_connection *nc, int ev, void *p) + { + if (MyCanLogUdpServer) + MyCanLogUdpServer->MongooseHandler(nc, ev, p); + else if (ev == MG_EV_ACCEPT) + { + ESP_LOGI(TAG, "Log service connection rejected (logger not running)"); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } + +canlog_udpserver::canlog_udpserver(std::string path, std::string format, canformat::canformat_serve_mode_t mode) + : canlog("udpserver", format, mode) + { + MyCanLogUdpServer = this; + m_path = path; + if (m_path.find(':') == std::string::npos) + { + m_path.insert(0, "udp://"); + } + m_isopen = false; + m_mgconn = NULL; + + #undef bind // Kludgy, but works + using std::placeholders::_1; + using std::placeholders::_2; + MyEvents.RegisterEvent(TAG,"ticker.10", std::bind(&canlog_udpserver::Ticker, this, _1, _2)); + } + +canlog_udpserver::~canlog_udpserver() + { + Close(); + MyCanLogUdpServer = NULL; + MyEvents.DeregisterEvent(TAG); + } + +bool canlog_udpserver::Open() + { + if (m_isopen) return true; + + ESP_LOGI(TAG, "Launching UDP server at %s",m_path.c_str()); + struct mg_mgr* mgr = MyNetManager.GetMongooseMgr(); + if (mgr != NULL) + { + if (MyNetManager.m_network_any) + { + m_mgconn = mg_bind(mgr, m_path.c_str(), tsMongooseHandler); + if (m_mgconn != NULL) + { + ESP_LOGI(TAG,"Listening with nc %p",m_mgconn); + m_isopen = true; + return true; + } + else + { + ESP_LOGE(TAG,"Could not listen on %s",m_path.c_str()); + return false; + } + } + else + { + ESP_LOGI(TAG,"Delay UDP server (as network manager not up)"); + return true; + } + } + else + { + ESP_LOGE(TAG,"Network manager is not available"); + return false; + } + } + +void canlog_udpserver::Close() + { + if (m_isopen) + { + if (m_connmap.size() > 0) + { + OvmsRecMutexLock lock(&m_cmmutex); + for (conn_map_t::iterator it=m_connmap.begin(); it!=m_connmap.end(); ++it) + { + it->first->flags |= MG_F_CLOSE_IMMEDIATELY; + delete it->second; + } + m_connmap.clear(); + } + ESP_LOGI(TAG, "Closed UDP server log: %s", GetStats().c_str()); + m_mgconn->flags |= MG_F_CLOSE_IMMEDIATELY; + m_mgconn = NULL; + m_isopen = false; + } + } + +std::string canlog_udpserver::GetInfo() + { + std::string result = canlog::GetInfo(); + result.append(" Path:"); + result.append(m_path); + return result; + } + +void canlog_udpserver::Ticker(std::string event, void* data) + { + OvmsRecMutexLock lock(&m_cmmutex); + + for (conn_map_t::iterator it=m_connmap.begin(); it!=m_connmap.end(); ++it) + { + udpcanlogconnection* clc = (udpcanlogconnection*)it->second; + if (clc->m_timeout < monotonictime) + { + // This client has timed out + ESP_LOGD(TAG,"Timed out connection from %s",clc->m_peer.c_str()); + m_connmap.erase(it); + delete clc; + } + } + } + +void canlog_udpserver::MongooseHandler(struct mg_connection *nc, int ev, void *p) + { + char addr[32]; + + switch (ev) + { + case MG_EV_RECV: + { + OvmsRecMutexLock lock(&m_cmmutex); + mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP|MG_SOCK_STRINGIFY_PORT); + + size_t used = nc->recv_mbuf.len; + + // Try to find an existing matching connection + udpcanlogconnection* clc = NULL; + for (conn_map_t::iterator it=m_connmap.begin(); it!=m_connmap.end(); ++it) + { + clc = (udpcanlogconnection*)it->second; + if (memcmp(&clc->m_sa, &nc->sa, sizeof(&nc->sa.sin)) == 0) + { + // We have found an existing one, so just handle and tickle it + ESP_LOGD(TAG,"Tickle connection from %s",addr); + used = clc->m_formatter->Serve((uint8_t*)nc->recv_mbuf.buf, used, clc); + if (used>0) mbuf_remove(&nc->recv_mbuf, used); + clc->Tickle(); + return; + } + } + + // No matching connection, so make a new one + ESP_LOGD(TAG,"New connection from %s",addr); + clc = new udpcanlogconnection(this, m_format, m_mode); + clc->m_nc = m_mgconn; + clc->m_sock = m_mgconn->sock; + memcpy(&clc->m_sa,&nc->sa.sin,sizeof(nc->sa.sin)); + clc->m_peer = std::string(addr); + m_connmap[&clc->m_fakenc] = clc; + std::string result = clc->m_formatter->getheader(); + if (result.length()>0) + { + mg_send(nc, (const char*)result.c_str(), result.length()); + } + used = clc->m_formatter->Serve((uint8_t*)nc->recv_mbuf.buf, used, clc); + if (used > 0) mbuf_remove(&nc->recv_mbuf, used); + + break; + } + default: + break; + } + } + +#endif // #ifdef CONFIG_OVMS_SC_GPL_MONGOOSE diff --git a/OVMS.V3/components/can/src/canlog_udpserver.h b/OVMS.V3/components/can/src/canlog_udpserver.h new file mode 100644 index 0000000..8d3e0de --- /dev/null +++ b/OVMS.V3/components/can/src/canlog_udpserver.h @@ -0,0 +1,80 @@ +/* +; Project: Open Vehicle Monitor System +; Module: CAN logging framework +; Date: 18th January 2018 +; +; (C) 2018 Michael Balzer +; (C) 2019 Mark Webb-Johnson +; +; Permission is hereby granted, free of charge, to any person obtaining a copy +; of this software and associated documentation files (the "Software"), to deal +; in the Software without restriction, including without limitation the rights +; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +; copies of the Software, and to permit persons to whom the Software is +; furnished to do so, subject to the following conditions: +; +; The above copyright notice and this permission notice shall be included in +; all copies or substantial portions of the Software. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +; THE SOFTWARE. +*/ + +#ifndef __CANLOG_UDP_SERVER_H__ +#define __CANLOG_UDP_SERVER_H__ + +#include +#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE + +#include "canlog.h" +#include "ovms_netmanager.h" +#include "ovms_mutex.h" + +class udpcanlogconnection: public canlogconnection + { + public: + udpcanlogconnection(canlog* logger, std::string format, canformat::canformat_serve_mode_t mode); + virtual ~udpcanlogconnection(); + + public: + virtual void OutputMsg(CAN_log_message_t& msg, std::string &result); + + public: + void Tickle(); + + public: + sock_t m_sock; // Our main listening UDP socket + struct sockaddr m_sa; // Our remote client address + mg_connection m_fakenc; // A fake nc, just as an index to us + uint32_t m_timeout; // Our timeout + }; + +class canlog_udpserver : public canlog + { + public: + canlog_udpserver(std::string path, std::string format, canformat::canformat_serve_mode_t mode); + virtual ~canlog_udpserver(); + + public: + virtual bool Open(); + virtual void Close(); + virtual std::string GetInfo(); + + public: + void MongooseHandler(struct mg_connection *nc, int ev, void *p); + void Ticker(std::string event, void* data); + + public: + struct mg_connection *m_mgconn; + + public: + std::string m_path; + }; + +#endif // #ifdef CONFIG_OVMS_SC_GPL_MONGOOSE +#endif // __CANLOG_UDP_SERVER_H__