/* * Copyright (C) 2010-2014,2018,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "APRSWriterThread.h" #include "DStarDefines.h" #include "Utils.h" #include "Defs.h" // #define DUMP_TX const unsigned int APRS_TIMEOUT = 10U; CAPRSWriterThread::CAPRSWriterThread(const wxString& callsign, const wxString& password, const wxString& address, const wxString& hostname, unsigned int port) : wxThread(wxTHREAD_JOINABLE), m_username(callsign), m_password(password), m_ssid(callsign), m_socket(hostname, port, address), m_queue(20U), m_exit(false), m_connected(false), m_reconnectTimer(1000U), m_tries(0U), m_APRSReadCallback(NULL), m_filter(wxT("")), m_clientName(wxT("ircDDBGateway")) { wxASSERT(!callsign.IsEmpty()); wxASSERT(!password.IsEmpty()); wxASSERT(!hostname.IsEmpty()); wxASSERT(port > 0U); m_username.SetChar(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); m_username.Trim(); m_username.MakeUpper(); m_ssid = m_ssid.SubString(LONG_CALLSIGN_LENGTH - 1U, 1); } CAPRSWriterThread::CAPRSWriterThread(const wxString& callsign, const wxString& password, const wxString& address, const wxString& hostname, unsigned int port, const wxString& filter, const wxString& clientName) : wxThread(wxTHREAD_JOINABLE), m_username(callsign), m_password(password), m_ssid(callsign), m_socket(hostname, port, address), m_queue(20U), m_exit(false), m_connected(false), m_reconnectTimer(1000U), m_tries(0U), m_APRSReadCallback(NULL), m_filter(filter), m_clientName(clientName) { wxASSERT(!callsign.IsEmpty()); wxASSERT(!password.IsEmpty()); wxASSERT(!hostname.IsEmpty()); wxASSERT(port > 0U); m_username.SetChar(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); m_username.Trim(); m_username.MakeUpper(); m_ssid = m_ssid.SubString(LONG_CALLSIGN_LENGTH - 1U, 1); } CAPRSWriterThread::~CAPRSWriterThread() { m_username.Clear(); m_password.Clear(); } bool CAPRSWriterThread::start() { Create(); Run(); return true; } void* CAPRSWriterThread::Entry() { wxLogMessage(wxT("Starting the APRS Writer thread")); m_connected = connect(); if (!m_connected) { wxLogError(wxT("Connect attempt to the APRS server has failed")); startReconnectionTimer(); } try { while (!m_exit) { if (!m_connected) { if (m_reconnectTimer.isRunning() && m_reconnectTimer.hasExpired()) { m_reconnectTimer.stop(); m_connected = connect(); if (!m_connected) { wxLogError(wxT("Reconnect attempt to the APRS server has failed")); startReconnectionTimer(); } } } if (m_connected) { m_tries = 0U; if(!m_queue.isEmpty()){ char* p = m_queue.getData(); wxString text(p, wxConvLocal); wxLogMessage(wxT("APRS ==> %s"), text.c_str()); ::strcat(p, "\r\n"); bool ret = m_socket.write((unsigned char*)p, ::strlen(p)); if (!ret) { m_connected = false; m_socket.close(); wxLogError(wxT("Connection to the APRS thread has failed")); startReconnectionTimer(); } delete[] p; } { wxString line; int length = m_socket.readLine(line, APRS_TIMEOUT); /*if (length == 0) wxLogWarning(wxT("No response from the APRS server after %u seconds"), APRS_TIMEOUT);*/ if (length < 0) { m_connected = false; m_socket.close(); wxLogError(wxT("Error when reading from the APRS server")); startReconnectionTimer(); } if(length > 0 && line.GetChar(0) != '#'//check if we have something and if that something is an APRS frame && m_APRSReadCallback != NULL)//do we have someone wanting an APRS Frame? { //wxLogMessage(wxT("Received APRS Frame : ") + line); m_APRSReadCallback(wxString(line)); } } } } if (m_connected) m_socket.close(); while (!m_queue.isEmpty()) { char* p = m_queue.getData(); delete[] p; } } catch (std::exception& e) { wxString message(e.what(), wxConvLocal); wxLogError(wxT("Exception raised in the APRS Writer thread - \"%s\""), message.c_str()); } catch (...) { wxLogError(wxT("Unknown exception raised in the APRS Writer thread")); } wxLogMessage(wxT("Stopping the APRS Writer thread")); return NULL; } void CAPRSWriterThread::setReadAPRSCallback(ReadAPRSFrameCallback cb) { m_APRSReadCallback = cb; } void CAPRSWriterThread::write(const char* data) { wxASSERT(data != NULL); if (!m_connected) return; unsigned int len = ::strlen(data); char* p = new char[len + 5U]; ::strcpy(p, data); m_queue.addData(p); } bool CAPRSWriterThread::isConnected() const { return m_connected; } void CAPRSWriterThread::stop() { m_exit = true; Wait(); } void CAPRSWriterThread::clock(unsigned int ms) { m_reconnectTimer.clock(ms); } bool CAPRSWriterThread::connect() { bool ret = m_socket.open(); if (!ret) return false; //wait for lgin banner int length; wxString serverResponse(wxT("")); length = m_socket.readLine(serverResponse, APRS_TIMEOUT); if (length == 0) { wxLogError(wxT("No reply from the APRS server after %u seconds"), APRS_TIMEOUT); m_socket.close(); return false; } wxLogMessage(wxT("Received login banner : %s"), serverResponse.c_str()); wxString filter(m_filter); if (filter.Length() > 0) filter.Prepend(wxT(" filter ")); wxString connectString = wxString::Format(wxT("user %s-%s pass %s vers %s%s\n"), m_username.c_str(), m_ssid.c_str(), m_password.c_str(), (m_clientName.Length() ? m_clientName : wxT("ircDDBGateway")).c_str(), filter.c_str()); //wxLogMessage(wxT("Connect String : ") + connectString); ret = m_socket.writeLine(connectString); if (!ret) { m_socket.close(); return false; } length = m_socket.readLine(serverResponse, APRS_TIMEOUT); if (length == 0) { wxLogError(wxT("No reply from the APRS server after %u seconds"), APRS_TIMEOUT); m_socket.close(); return false; } if (length < 0) { wxLogError(wxT("Error when reading from the APRS server")); m_socket.close(); return false; } wxLogMessage(wxT("Response from APRS server: %s"), serverResponse.c_str()); wxLogMessage(wxT("Connected to the APRS server")); return true; } void CAPRSWriterThread::startReconnectionTimer() { // Clamp at a ten minutes reconnect time m_tries++; if (m_tries > 10U) m_tries = 10U; m_reconnectTimer.setTimeout(m_tries * 60U); m_reconnectTimer.start(); }