From 5a5bfc9e89a18558d740fb4d3a10730d32115d36 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 19 Sep 2018 15:09:28 +0100 Subject: [PATCH 1/2] Revert "First stage of CCS removal." This reverts commit 9669d865b5be3a8a0f0bcc92b1a3bc394be34d45. --- CHANGES.txt | 5 - Common/AMBEData.cpp | 68 +++- Common/AMBEData.h | 4 +- Common/AudioUnit.cpp | 6 +- Common/CCSCallback.h | 44 +++ Common/CCSData.cpp | 192 +++++++++ Common/CCSData.h | 74 ++++ Common/CCSHandler.cpp | 700 +++++++++++++++++++++++++++++++++ Common/CCSHandler.h | 155 ++++++++ Common/CCSProtocolHandler.cpp | 235 +++++++++++ Common/CCSProtocolHandler.h | 81 ++++ Common/Common.vcxproj | 7 + Common/Common.vcxproj.filters | 21 + Common/ConnectData.cpp | 69 +++- Common/ConnectData.h | 4 +- Common/DStarDefines.h | 8 +- Common/DTMF.cpp | 98 ++++- Common/DTMF.h | 3 +- Common/Defs.h | 10 +- Common/HeaderData.cpp | 32 +- Common/HeaderData.h | 4 +- Common/HeardData.cpp | 41 +- Common/HeardData.h | 4 +- Common/IRCDDBGatewayConfig.cpp | 29 +- Common/IRCDDBGatewayConfig.h | 6 +- Common/Makefile | 2 +- Common/PollData.cpp | 35 +- Common/PollData.h | 4 +- Common/RemoteHandler.cpp | 3 +- Common/RepeaterHandler.cpp | 347 +++++++++++++++- Common/RepeaterHandler.h | 16 +- Data/CCS_Hosts.txt | 15 + Data/Makefile | 1 + ircDDBGateway32.nsi | 1 + ircDDBGateway64.nsi | 1 + 35 files changed, 2286 insertions(+), 39 deletions(-) create mode 100644 Common/CCSCallback.h create mode 100644 Common/CCSData.cpp create mode 100644 Common/CCSData.h create mode 100644 Common/CCSHandler.cpp create mode 100644 Common/CCSHandler.h create mode 100644 Common/CCSProtocolHandler.cpp create mode 100644 Common/CCSProtocolHandler.h create mode 100644 Data/CCS_Hosts.txt diff --git a/CHANGES.txt b/CHANGES.txt index 8afb29f..0661426 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1481,8 +1481,3 @@ Simplify the Linux build. -------- Support the GPS data from the Kenwood TH-D74. - -201809xx --------- - -Remove the CCS support. diff --git a/Common/AMBEData.cpp b/Common/AMBEData.cpp index bc24b1c..c30aacd 100644 --- a/Common/AMBEData.cpp +++ b/Common/AMBEData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2013,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2010-2013 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 @@ -203,6 +203,28 @@ bool CAMBEData::setDCSData(const unsigned char *data, unsigned int length, const return true; } +bool CAMBEData::setCCSData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 100U); + + m_header.setCCSData(data, length, yourAddress, yourPort, myPort); + + m_id = data[44] * 256U + data[43]; + + m_outSeq = data[45]; + + ::memcpy(m_data, data + 46U, DV_FRAME_LENGTH_BYTES); + + m_rptSeq = data[60] * 65536U + data[59] * 256U + data[58]; + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + + return true; +} + unsigned int CAMBEData::getIcomRepeaterData(unsigned char *data, unsigned int length) const { wxASSERT(data != NULL); @@ -408,6 +430,50 @@ unsigned int CAMBEData::getDCSData(unsigned char* data, unsigned int length) con return 100U; } +unsigned int CAMBEData::getCCSData(unsigned char* data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 100U); + + ::memset(data, 0x00U, 100U); + + data[0] = '0'; + data[1] = '0'; + data[2] = '0'; + data[3] = '1'; + + data[43] = m_id % 256U; // Unique session id + data[44] = m_id / 256U; + + data[45] = m_outSeq; + + ::memcpy(data + 46U, m_data, DV_FRAME_LENGTH_BYTES); + + if (isEnd()) { + data[55] = 0x55U; + data[56] = 0x55U; + data[57] = 0x55U; + } + + data[58] = (m_rptSeq >> 0) & 0xFFU; + data[59] = (m_rptSeq >> 8) & 0xFFU; + data[60] = (m_rptSeq >> 16) & 0xFFU; + + data[61] = 0x01U; + data[62] = 0x00U; + + data[63] = 0x21U; + + for (unsigned int i = 0U; i < m_text.Len(); i++) + data[64 + i] = m_text.GetChar(i); + + data[93U] = 0x36U; + + m_header.getCCSData(data, 100U); + + return 100U; +} + unsigned int CAMBEData::getId() const { return m_id; diff --git a/Common/AMBEData.h b/Common/AMBEData.h index 62fb8ff..773df7c 100644 --- a/Common/AMBEData.h +++ b/Common/AMBEData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2013,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2010-2013 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 @@ -41,12 +41,14 @@ public: bool setDExtraData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); bool setDPlusData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); bool setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); + bool setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); unsigned int getIcomRepeaterData(unsigned char* data, unsigned int length) const; unsigned int getHBRepeaterData(unsigned char* data, unsigned int length) const; unsigned int getDExtraData(unsigned char* data, unsigned int length) const; unsigned int getDPlusData(unsigned char* data, unsigned int length) const; unsigned int getDCSData(unsigned char* data, unsigned int length) const; + unsigned int getCCSData(unsigned char* data, unsigned int length) const; unsigned int getG2Data(unsigned char* data, unsigned int length) const; unsigned int getId() const; diff --git a/Common/AudioUnit.cpp b/Common/AudioUnit.cpp index 36698ad..da9774b 100644 --- a/Common/AudioUnit.cpp +++ b/Common/AudioUnit.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2011-2014 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 @@ -295,7 +295,8 @@ void CAudioUnit::spellReflector(unsigned int id, const wxString &reflector) if (c == wxT(' ')) return; - if (m_linkStatus == LS_LINKING_DCS || m_linkStatus == LS_LINKED_DCS) { + if (m_linkStatus == LS_LINKING_DCS || m_linkStatus == LS_LINKED_DCS || + m_linkStatus == LS_LINKING_CCS || m_linkStatus == LS_LINKED_CCS) { lookup(id, wxString(c)); return; } @@ -466,6 +467,7 @@ void CAudioUnit::sendStatus(LINK_STATUS status, const wxString& reflector, const case LS_NONE: lookup(id, wxT("notlinked")); break; + case LS_LINKED_CCS: case LS_LINKED_DCS: case LS_LINKED_DPLUS: case LS_LINKED_DEXTRA: diff --git a/Common/CCSCallback.h b/Common/CCSCallback.h new file mode 100644 index 0000000..4fc8864 --- /dev/null +++ b/Common/CCSCallback.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef CCSCallback_H +#define CCSCallback_H + +#include "DStarDefines.h" +#include "HeaderData.h" +#include "AMBEData.h" +#include "Defs.h" + +#include + +class ICCSCallback { +public: + virtual bool process(CHeaderData& header, DIRECTION direction, AUDIO_SOURCE source) = 0; + + virtual bool process(CAMBEData& data, DIRECTION direction, AUDIO_SOURCE source) = 0; + + virtual void ccsLinkMade(const wxString& callsign, DIRECTION direction) = 0; + + virtual void ccsLinkFailed(const wxString& dtmf, DIRECTION direction) = 0; + + virtual void ccsLinkEnded(const wxString& callsign, DIRECTION direction) = 0; + +private: +}; + +#endif diff --git a/Common/CCSData.cpp b/Common/CCSData.cpp new file mode 100644 index 0000000..a589f62 --- /dev/null +++ b/Common/CCSData.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2013 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 "DStarDefines.h" +#include "CCSData.h" +#include "Utils.h" + +CCCSData::CCCSData(const wxString& local, double latitude, double longitude, double frequency, double offset, const wxString& description1, const wxString& description2, const wxString& url, CC_TYPE type) : +m_local(local), +m_remote(), +m_latitude(latitude), +m_longitude(longitude), +m_frequency(frequency), +m_offset(offset), +m_description1(description1), +m_description2(description2), +m_url(url), +m_type(type), +m_yourAddress(), +m_yourPort(0U), +m_myPort(0U) +{ +} + +CCCSData::CCCSData(const wxString& local, const wxString& remote, CC_TYPE type) : +m_local(local), +m_remote(remote), +m_latitude(0.0), +m_longitude(0.0), +m_frequency(0.0), +m_offset(0.0), +m_description1(), +m_description2(), +m_url(), +m_type(type), +m_yourAddress(), +m_yourPort(0U), +m_myPort(0U) +{ +} + +CCCSData::CCCSData() : +m_local(), +m_remote(), +m_latitude(0.0), +m_longitude(0.0), +m_frequency(0.0), +m_offset(0.0), +m_description1(), +m_description2(), +m_url(), +m_type(), +m_yourAddress(), +m_yourPort(0U), +m_myPort(0U) +{ +} + +CCCSData::~CCCSData() +{ +} + +bool CCCSData::setCCSData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) +{ + wxASSERT(data != NULL); + + switch (length) { + case 100U: + m_remote = wxString((char*)(data + 0U), wxConvLocal, LONG_CALLSIGN_LENGTH); + + if (::memcmp(data + 8U, "0001", 4U) == 0) { + m_type = CT_TERMINATE; + } else { + // CUtils::dump(wxT("Invalid CCS packet"), data, length); + return false; + } + + m_local = wxString((char*)(data + 12U), wxConvLocal, LONG_CALLSIGN_LENGTH); + break; + + case 20U: + if (::memcmp(data + 0U, "DTMF_CALL:", 10U) == 0) { + m_type = CT_DTMFFOUND; + } else { + CUtils::dump(wxT("Invalid CCS packet"), data, length); + return false; + } + + m_remote = wxString((char*)(data + 10U), wxConvLocal, LONG_CALLSIGN_LENGTH); + break; + + case 17U: + if (::memcmp(data + 0U, "NODTMFCALL", 10U) == 0) { + m_type = CT_DTMFNOTFOUND; + } else { + CUtils::dump(wxT("Invalid CCS packet"), data, length); + return false; + } + break; + + default: + CUtils::dump(wxT("Invalid CCS packet"), data, length); + return false; + } + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + + return true; +} + +unsigned int CCCSData::getCCSData(unsigned char* data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 133U); + + if (m_type == CT_TERMINATE) { + ::memset(data, ' ', 38U); + + for (unsigned int i = 0U; i < m_remote.Len() && i < LONG_CALLSIGN_LENGTH; i++) + data[i + 0U] = m_remote.GetChar(i); + + ::memcpy(data + 8U, "0001", 4U); + + for (unsigned int i = 0U; i < m_local.Len() && i < LONG_CALLSIGN_LENGTH; i++) + data[i + 12U] = m_local.GetChar(i); + + return 38U; + } else if (m_type == CT_INFO) { + wxString buffer; + buffer.Printf(wxT("IRPT%.7s %s%-10.4lf%-10.4lf%-10.4lf%-10.4lf%-20s%-20s%-40s"), m_local.Mid(0U, LONG_CALLSIGN_LENGTH - 1U).c_str(), m_local.Mid(LONG_CALLSIGN_LENGTH - 1U, 1U).c_str(), m_latitude, m_longitude, m_frequency, m_offset, m_description1.c_str(), m_description2.c_str(), m_url.c_str()); + + for (unsigned int i = 0U; i < buffer.Len() && i < 133U; i++) + data[i] = buffer.GetChar(i); + + return 133U; + } + + return 0U; +} + +wxString CCCSData::getLocal() const +{ + return m_local; +} + +wxString CCCSData::getRemote() const +{ + return m_remote; +} + +CC_TYPE CCCSData::getType() const +{ + return m_type; +} + +void CCCSData::setDestination(const in_addr& address, unsigned int port) +{ + m_yourAddress = address; + m_yourPort = port; +} + +in_addr CCCSData::getYourAddress() const +{ + return m_yourAddress; +} + +unsigned int CCCSData::getYourPort() const +{ + return m_yourPort; +} + +unsigned int CCCSData::getMyPort() const +{ + return m_myPort; +} diff --git a/Common/CCSData.h b/Common/CCSData.h new file mode 100644 index 0000000..1488dcd --- /dev/null +++ b/Common/CCSData.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef CCSData_H +#define CCSData_H + +#include + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +enum CC_TYPE { + CT_TERMINATE, + CT_DTMFNOTFOUND, + CT_DTMFFOUND, + CT_INFO +}; + +class CCCSData { +public: + CCCSData(const wxString& local, double latitude, double longitude, double frequency, double offset, const wxString& description1, const wxString& description2, const wxString& url, CC_TYPE type); + CCCSData(const wxString& local, const wxString& remote, CC_TYPE type); + CCCSData(); + ~CCCSData(); + + bool setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); + + unsigned int getCCSData(unsigned char* data, unsigned int length) const; + + void setDestination(const in_addr& address, unsigned int port); + + wxString getLocal() const; + wxString getRemote() const; + CC_TYPE getType() const; + + in_addr getYourAddress() const; + unsigned int getYourPort() const; + unsigned int getMyPort() const; + +private: + wxString m_local; + wxString m_remote; + double m_latitude; + double m_longitude; + double m_frequency; + double m_offset; + wxString m_description1; + wxString m_description2; + wxString m_url; + CC_TYPE m_type; + in_addr m_yourAddress; + unsigned int m_yourPort; + unsigned int m_myPort; +}; + +#endif diff --git a/Common/CCSHandler.cpp b/Common/CCSHandler.cpp new file mode 100644 index 0000000..470d648 --- /dev/null +++ b/Common/CCSHandler.cpp @@ -0,0 +1,700 @@ +/* + * Copyright (C) 2013,2014 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 "RepeaterHandler.h" +#include "DStarDefines.h" +#include "CCSHandler.h" +#include "Utils.h" + + +CCCSHandler** CCCSHandler::m_handlers = NULL; + +unsigned int CCCSHandler::m_count = 0U; + +wxString CCCSHandler::m_localAddress; +CHeaderLogger* CCCSHandler::m_headerLogger = NULL; + +wxString CCCSHandler::m_ccsHost; + +CCCSCache_t CCCSHandler::m_cache; +wxMutex CCCSHandler::m_mutex; + +bool CCCSHandler::m_stateChange = false; + + +void CCCSHandler::initialise(unsigned int count) +{ + wxASSERT(count > 0U); + + m_count = count; + m_handlers = new CCCSHandler*[m_count]; + + for (unsigned int i = 0U; i < m_count; i++) + m_handlers[i] = NULL; +} + +void CCCSHandler::setLocalAddress(const wxString& address) +{ + m_localAddress = address; +} + +void CCCSHandler::setHeaderLogger(CHeaderLogger* logger) +{ + m_headerLogger = logger; +} + +void CCCSHandler::setHost(const wxString& host) +{ + m_ccsHost = host; +} + +void CCCSHandler::process() +{ + for (unsigned int i = 0U; i < m_count; i++) { + if (m_handlers[i] != NULL) + m_handlers[i]->processInt(); + } +} + +void CCCSHandler::disconnect() +{ + for (unsigned int i = 0U; i < m_count; i++) { + if (m_handlers[i] != NULL) + m_handlers[i]->disconnectInt(); + } +} + +void CCCSHandler::clock(unsigned int ms) +{ + for (unsigned int i = 0U; i < m_count; i++) { + if (m_handlers[i] != NULL) + m_handlers[i]->clockInt(ms); + } +} + +void CCCSHandler::getInfo(ICCSCallback* handler, CRemoteRepeaterData& data) +{ + wxASSERT(handler != NULL); + + for (unsigned int i = 0U; i < m_count; i++) { + CCCSHandler* ccs = m_handlers[i]; + if (ccs != NULL && ccs->m_handler == handler && ccs->m_state == CS_ACTIVE) + data.addLink(ccs->m_yourCall, PROTO_CCS, true, ccs->m_direction, false); + } +} + +wxString CCCSHandler::getIncoming(const wxString& callsign) +{ + wxString incoming; + + for (unsigned int i = 0U; i < m_count; i++) { + CCCSHandler* handler = m_handlers[i]; + if (handler != NULL && handler->m_direction == DIR_INCOMING && handler->m_state == CS_ACTIVE && handler->m_callsign.IsSameAs(callsign)) { + incoming.Append(handler->m_yourCall); + incoming.Append(wxT(" ")); + } + } + + return incoming; +} + +void CCCSHandler::finalise() +{ + for (unsigned int i = 0U; i < m_count; i++) + delete m_handlers[i]; + + delete[] m_handlers; +} + +CCCSHandler::CCCSHandler(ICCSCallback* handler, const wxString& callsign, unsigned int delay, double latitude, double longitude, double frequency, double offset, const wxString& description1, const wxString& description2, const wxString& url, unsigned int localPort) : +m_handler(handler), +m_callsign(callsign), +m_reflector(), +m_latitude(latitude), +m_longitude(longitude), +m_frequency(frequency), +m_offset(offset), +m_description1(description1), +m_description2(description2), +m_url(url), +m_ccsAddress(), +m_protocol(localPort, m_localAddress), +m_state(CS_DISABLED), +m_local(), +m_announceTimer(1000U, 20U), // 20 seconds +m_inactivityTimer(1000U, 300U), // 5 minutes +m_pollInactivityTimer(1000U, 60U), // 60 seconds +m_pollTimer(1000U, 10U), // 10 seconds +m_waitTimer(1000U, delay), +m_tryTimer(1000U, 1U), // 1 second +m_tryCount(0U), +m_id(0x00U), +m_seqNo(0U), +m_time(), +m_direction(DIR_OUTGOING), +m_yourCall(), +m_myCall1(), +m_myCall2(), +m_rptCall1() +{ + wxASSERT(handler != NULL); + + // Add to the global list + for (unsigned int i = 0U; i < m_count; i++) { + if (m_handlers[i] == NULL) { + m_handlers[i] = this; + break; + } + } +} + +CCCSHandler::~CCCSHandler() +{ +} + +void CCCSHandler::setReflector(const wxString& callsign) +{ + m_reflector = callsign; + + if (m_reflector.IsEmpty()) + m_reflector = wxT(" "); +} + +void CCCSHandler::processInt() +{ + if (m_state == CS_DISABLED) + return; + + for (;;) { + CCS_TYPE type = m_protocol.read(); + + switch (type) { + case CT_DATA: { + CAMBEData* data = m_protocol.readData(); + if (data != NULL) { + process(*data); + delete data; + } + } + break; + + case CT_POLL: { + CPollData* poll = m_protocol.readPoll(); + if (poll != NULL) { + process(*poll); + delete poll; + } + } + break; + + case CT_CONNECT: { + CConnectData* connect = m_protocol.readConnect(); + if (connect != NULL) { + process(*connect); + delete connect; + } + } + break; + + case CT_MISC: { + CCCSData* data = m_protocol.readMisc(); + if (data != NULL) { + process(*data); + delete data; + } + } + break; + + default: + return; + } + } +} + +void CCCSHandler::process(CAMBEData& data) +{ + CHeaderData& header = data.getHeader(); + wxString myCall1 = header.getMyCall1(); + wxString rptCall1 = header.getRptCall1(); + wxString yourCall = header.getYourCall(); + unsigned int seqNo = data.getSeq(); + unsigned int id = data.getId(); + + if (m_state != CS_CONNECTED && m_state != CS_ACTIVE) + return; + + // This is a new incoming CCS call + if (m_state == CS_CONNECTED) { + m_yourCall = myCall1; + m_local = yourCall; + m_rptCall1 = rptCall1; + m_direction = DIR_INCOMING; + m_time = ::time(NULL); + m_state = CS_ACTIVE; + m_stateChange = true; + m_inactivityTimer.start(); + + m_handler->ccsLinkMade(m_yourCall, m_direction); + + wxLogMessage(wxT("CCS: New incoming link to %s from %s @ %s"), m_local.c_str(), m_yourCall.c_str(), m_rptCall1.c_str()); + } else { + if (!m_yourCall.IsSameAs(myCall1) && !m_rptCall1.IsSameAs(rptCall1)) { + wxLogMessage(wxT("CCS: Rejecting new incoming CCS link from %s @ %s to %s"), myCall1.c_str(), rptCall1.c_str(), yourCall.c_str()); + + CCCSData data(yourCall, myCall1, CT_TERMINATE); + data.setDestination(m_ccsAddress, CCS_PORT); + + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + + return; + } + + // Allow for the fact that the distant repeater may change during the QSO + if (m_yourCall.IsSameAs(myCall1) && !m_rptCall1.IsSameAs(rptCall1)) { + wxLogMessage(wxT("CCS: %s has moved from repeater %s to %s"), m_yourCall.c_str(), m_rptCall1.c_str(), rptCall1.c_str()); + m_rptCall1 = rptCall1; + } + } + + m_pollInactivityTimer.start(); + m_inactivityTimer.start(); + + if (m_id != id) { + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("CCS"), header); + + header.setCQCQCQ(); + m_handler->process(header, DIR_INCOMING, AS_CCS); + + m_id = id; + } else if (seqNo == 0U) { + header.setCQCQCQ(); + m_handler->process(header, DIR_INCOMING, AS_DUP); + } + + m_handler->process(data, DIR_INCOMING, AS_CCS); +} + +void CCCSHandler::process(CCCSData& data) +{ + CC_TYPE type = data.getType(); + + switch (type) { + case CT_TERMINATE: + if (m_state == CS_ACTIVE) { + wxLogMessage(wxT("CCS: Link between %s and %s has been terminated"), data.getLocal().c_str(), data.getRemote().c_str()); + m_stateChange = true; + m_state = CS_CONNECTED; + m_inactivityTimer.stop(); + m_handler->ccsLinkEnded(data.getRemote(), m_direction); + } + break; + + case CT_DTMFNOTFOUND: + wxLogMessage(wxT("CCS: Cannot map %s to a callsign"), m_yourCall.c_str()); + m_stateChange = true; + m_state = CS_CONNECTED; + m_inactivityTimer.stop(); + m_handler->ccsLinkFailed(m_yourCall, m_direction); + break; + + case CT_DTMFFOUND: + wxLogMessage(wxT("CCS: Mapped %s to %s, added to the cache"), m_yourCall.c_str(), data.getRemote().c_str()); + addToCache(m_yourCall, data.getRemote()); + m_stateChange = true; + m_yourCall = data.getRemote(); + m_rptCall1 = data.getRemote(); + m_handler->ccsLinkMade(m_yourCall, m_direction); + break; + + default: + break; + } +} + +void CCCSHandler::process(CPollData&) +{ + m_pollInactivityTimer.start(); +} + +void CCCSHandler::process(CConnectData& connect) +{ + CD_TYPE type = connect.getType(); + + if (type == CT_ACK && m_state == CS_CONNECTING) { + wxLogMessage(wxT("CCS: %s connected to server %s"), m_callsign.c_str(), m_ccsHost.c_str()); + + m_announceTimer.start(); + m_pollInactivityTimer.start(); + m_pollTimer.start(); + m_tryTimer.stop(); + + // Give our location, frequency, etc + CCCSData data(m_callsign, m_latitude, m_longitude, m_frequency, m_offset, m_description1, m_description2, m_url, CT_INFO); + data.setDestination(m_ccsAddress, CCS_PORT); + m_protocol.writeMisc(data); + + m_state = CS_CONNECTED; + + return; + } + + if (type == CT_NAK && m_state == CS_CONNECTING) { + wxLogMessage(wxT("CCS: Connection refused for %s"), m_callsign.c_str()); + m_tryTimer.stop(); + m_state = CS_DISABLED; + return; + } +} + +bool CCCSHandler::connect() +{ + // Is CCS disabled? + if (m_localAddress.IsSameAs(wxT("127.0.0.1"))) + return false; + + // Can we resolve the CCS server address? + m_ccsAddress = CUDPReaderWriter::lookup(m_ccsHost); + if (m_ccsAddress.s_addr == INADDR_NONE) { + wxLogError(wxT("CCS: Unable to find the IP address for %s"), m_ccsHost.c_str()); + return false; + } + + bool res = m_protocol.open(); + if (!res) + return false; + + wxLogMessage(wxT("CCS: Opening UDP port %u for %s"), m_protocol.getPort(), m_callsign.c_str()); + + m_waitTimer.start(); + + m_state = CS_CONNECTING; + + return true; +} + +void CCCSHandler::disconnectInt() +{ + if (m_state == CS_CONNECTED || m_state == CS_ACTIVE) { + CConnectData connect(m_callsign, CT_UNLINK, m_ccsAddress, CCS_PORT); + m_protocol.writeConnect(connect); + } + + m_announceTimer.stop(); + m_pollInactivityTimer.stop(); + m_inactivityTimer.stop(); + m_pollTimer.stop(); + m_tryTimer.stop(); + + if (m_state != CS_DISABLED) + m_protocol.close(); + + m_state = CS_DISABLED; +} + +void CCCSHandler::startLink(const wxString& dtmf, const wxString& user, const wxString& type) +{ + if (m_state != CS_CONNECTED) + return; + + wxString callsign = findInCache(dtmf); + if (!callsign.IsEmpty()) { + wxLogMessage(wxT("CCS: New outgoing link to %s/%s via %s by %s"), dtmf.c_str(), callsign.c_str(), type.c_str(), user.c_str()); + m_handler->ccsLinkMade(callsign, m_direction); + m_yourCall = callsign; + m_rptCall1 = callsign; + } else { + wxLogMessage(wxT("CCS: New outgoing link to %s via %s by %s"), dtmf.c_str(), type.c_str(), user.c_str()); + m_yourCall = dtmf; + m_yourCall.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + m_rptCall1.Clear(); + } + + m_local = user; + m_seqNo = 0U; + + m_time = ::time(NULL); + m_stateChange = true; + m_state = CS_ACTIVE; + m_direction = DIR_OUTGOING; + m_inactivityTimer.start(); +} + +void CCCSHandler::stopLink(const wxString& user, const wxString& type) +{ + if (m_state != CS_ACTIVE) + return; + + if (!user.IsEmpty() && !type.IsEmpty()) + wxLogMessage(wxT("CCS: Link to %s from %s has been terminated via %s by %s"), m_yourCall.c_str(), m_local.c_str(), type.c_str(), user.c_str()); + + CCCSData data(m_local, m_yourCall, CT_TERMINATE); + data.setDestination(m_ccsAddress, CCS_PORT); + + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + + m_stateChange = true; + m_state = CS_CONNECTED; + m_inactivityTimer.stop(); + + m_handler->ccsLinkEnded(m_yourCall, m_direction); +} + +void CCCSHandler::unlink(const wxString& callsign) +{ + if (m_state != CS_ACTIVE) + return; + + if (!m_yourCall.IsSameAs(callsign)) + return; + + wxLogMessage(wxT("CCS: Link to %s from %s has been terminated by command"), m_yourCall.c_str(), m_local.c_str()); + + CCCSData data(m_local, m_yourCall, CT_TERMINATE); + data.setDestination(m_ccsAddress, CCS_PORT); + + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + + m_stateChange = true; + m_state = CS_CONNECTED; + m_inactivityTimer.stop(); + + m_handler->ccsLinkEnded(m_yourCall, m_direction); +} + +void CCCSHandler::writeHeard(CHeaderData& header) +{ + if (m_state != CS_CONNECTED && m_state != CS_ACTIVE) + return; + + CHeardData heard(header, m_callsign, m_reflector); + heard.setDestination(m_ccsAddress, CCS_PORT); + m_protocol.writeHeard(heard); +} + +void CCCSHandler::writeHeader(CHeaderData& header) +{ + m_myCall1 = header.getMyCall1(); + m_myCall2 = header.getMyCall2(); + + m_seqNo = 0U; +} + +void CCCSHandler::writeAMBE(CAMBEData& data) +{ + if (m_state != CS_ACTIVE) + return; + + CAMBEData temp(data); + + CHeaderData& header = temp.getHeader(); + header.setMyCall1(m_myCall1); + header.setMyCall2(m_myCall2); + header.setYourCall(m_yourCall); + header.setRptCall1(m_callsign); + header.setRptCall2(m_reflector); + + temp.setRptSeq(m_seqNo++); + temp.setDestination(m_ccsAddress, CCS_PORT); + m_protocol.writeData(temp); +} + +CCS_STATUS CCCSHandler::getStatus() const +{ + return m_state; +} + +void CCCSHandler::clockInt(unsigned int ms) +{ + m_announceTimer.clock(ms); + m_pollInactivityTimer.clock(ms); + m_inactivityTimer.clock(ms); + m_pollTimer.clock(ms); + m_waitTimer.clock(ms); + m_tryTimer.clock(ms); + + if (m_pollInactivityTimer.isRunning() && m_pollInactivityTimer.hasExpired()) { + wxLogMessage(wxT("CCS: Connection has failed (poll inactivity) for %s, reconnecting"), m_callsign.c_str()); + + m_announceTimer.stop(); + m_pollInactivityTimer.stop(); + m_inactivityTimer.stop(); + m_pollTimer.stop(); + + if (m_state == CS_ACTIVE) { + m_stateChange = true; + m_handler->ccsLinkEnded(m_yourCall, m_direction); + } + + m_waitTimer.start(); + + m_state = CS_CONNECTING; + + return; + } + + if (m_tryTimer.isRunning() && m_tryTimer.hasExpired()) { + CConnectData connect(m_callsign, CT_LINK1, m_ccsAddress, CCS_PORT); + if (m_latitude != 0.0 && m_longitude != 0.0) { + wxString locator = CUtils::latLonToLoc(m_latitude, m_longitude); + connect.setLocator(locator); + } + m_protocol.writeConnect(connect); + + unsigned int t = calcBackoff(); + m_tryTimer.start(t); + } + + if (m_pollTimer.isRunning() && m_pollTimer.hasExpired()) { + CPollData poll(m_callsign, m_ccsAddress, CCS_PORT); + m_protocol.writePoll(poll); + + m_pollTimer.start(); + } + + if (m_inactivityTimer.isRunning() && m_inactivityTimer.hasExpired()) { + wxLogMessage(wxT("CCS: Activity timeout on link for %s"), m_callsign.c_str(), m_callsign.c_str()); + + CCCSData data(m_local, m_yourCall, CT_TERMINATE); + data.setDestination(m_ccsAddress, CCS_PORT); + + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + m_protocol.writeMisc(data); + + m_stateChange = true; + m_state = CS_CONNECTED; + m_inactivityTimer.stop(); + + m_handler->ccsLinkEnded(m_yourCall, m_direction); + } + + if (m_waitTimer.isRunning() && m_waitTimer.hasExpired()) { + CConnectData connect(m_callsign, CT_LINK1, m_ccsAddress, CCS_PORT); + if (m_latitude != 0.0 && m_longitude != 0.0) { + wxString locator = CUtils::latLonToLoc(m_latitude, m_longitude); + connect.setLocator(locator); + } + m_protocol.writeConnect(connect); + + m_tryTimer.start(1U); + m_tryCount = 1U; + + m_waitTimer.stop(); + } + + if (m_announceTimer.isRunning() && m_announceTimer.hasExpired()) { + CHeaderData header; + header.setMyCall1(m_callsign.Left(LONG_CALLSIGN_LENGTH - 1U)); + CHeardData heard(header, m_callsign, wxEmptyString); + heard.setDestination(m_ccsAddress, CCS_PORT); + m_protocol.writeHeard(heard); + + m_announceTimer.start(3600U); + } +} + +unsigned int CCCSHandler::calcBackoff() +{ + if (m_tryCount >= 7U) { + m_tryCount++; + return 60U; + } + + unsigned int timeout = 1U; + + for (unsigned int i = 0U; i < m_tryCount; i++) + timeout *= 2U; + + m_tryCount++; + + if (timeout > 60U) + return 60U; + else + return timeout; +} + +bool CCCSHandler::stateChange() +{ + bool stateChange = m_stateChange; + + m_stateChange = false; + + return stateChange; +} + +void CCCSHandler::writeStatus(wxFFile& file) +{ + for (unsigned int i = 0U; i < m_count; i++) { + CCCSHandler* handler = m_handlers[i]; + if (handler != NULL) { + struct tm* tm = ::gmtime(&handler->m_time); + + switch (handler->m_direction) { + case DIR_OUTGOING: + if (handler->m_state == CS_ACTIVE) { + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: CCS link - Rptr: %s Remote: %s Dir: Outgoing\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + handler->m_callsign.c_str(), handler->m_yourCall.c_str()); + file.Write(text); + } + break; + + case DIR_INCOMING: + if (handler->m_state == CS_ACTIVE) { + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: CCS link - Rptr: %s Remote: %s Dir: Incoming\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + handler->m_callsign.c_str(), handler->m_yourCall.c_str()); + file.Write(text); + } + break; + } + } + } +} + +void CCCSHandler::addToCache(const wxString& dtmf, const wxString& callsign) +{ + wxMutexLocker locker(m_mutex); + + m_cache[dtmf] = callsign; +} + +wxString CCCSHandler::findInCache(const wxString& dtmf) +{ + wxMutexLocker locker(m_mutex); + + return m_cache[dtmf]; +} diff --git a/Common/CCSHandler.h b/Common/CCSHandler.h new file mode 100644 index 0000000..81f5a1a --- /dev/null +++ b/Common/CCSHandler.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef CCSHandler_H +#define CCSHandler_H + +#include "CCSProtocolHandler.h" +#include "DStarDefines.h" +#include "HeaderLogger.h" +#include "ConnectData.h" +#include "CCSCallback.h" +#include "AMBEData.h" +#include "PollData.h" +#include "Timer.h" +#include "Defs.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include +#include + +enum CCS_STATUS { + CS_DISABLED, + CS_CONNECTING, + CS_CONNECTED, + CS_ACTIVE +}; + +WX_DECLARE_STRING_HASH_MAP(wxString, CCCSCache_t); + +class CCCSHandler { +public: + CCCSHandler(ICCSCallback* handler, const wxString& callsign, unsigned int delay, double latitude, double longitude, double frequency, double offset, const wxString& description1, const wxString& description2, const wxString& url, unsigned int localPort); + ~CCCSHandler(); + + bool connect(); + + void writeHeard(CHeaderData& header); + void writeHeader(CHeaderData& header); + void writeAMBE(CAMBEData& data); + + void startLink(const wxString& dtmf, const wxString& user, const wxString& type); + void stopLink(const wxString& user = wxEmptyString, const wxString& type = wxEmptyString); + + void unlink(const wxString& callsign); + + void setReflector(const wxString& callsign = wxEmptyString); + + CCS_STATUS getStatus() const; + + static void disconnect(); + + static void initialise(unsigned int count); + + static void process(); + + static void clock(unsigned int ms); + + static void setHeaderLogger(CHeaderLogger* logger); + + static void setLocalAddress(const wxString& address); + + static void setHost(const wxString& host); + + static bool stateChange(); + static void writeStatus(wxFFile& file); + + static void getInfo(ICCSCallback* handler, CRemoteRepeaterData& data); + + static wxString getIncoming(const wxString& callsign); + + static void finalise(); + +protected: + void clockInt(unsigned int ms); + + void processInt(); + + void disconnectInt(); + +private: + static CCCSHandler** m_handlers; + static unsigned int m_count; + + static wxString m_localAddress; + static CHeaderLogger* m_headerLogger; + + static wxString m_ccsHost; + + static CCCSCache_t m_cache; + static wxMutex m_mutex; + + static bool m_stateChange; + + ICCSCallback* m_handler; + wxString m_callsign; + wxString m_reflector; + double m_latitude; + double m_longitude; + double m_frequency; + double m_offset; + wxString m_description1; + wxString m_description2; + wxString m_url; + in_addr m_ccsAddress; + CCCSProtocolHandler m_protocol; + CCS_STATUS m_state; + wxString m_local; + CTimer m_announceTimer; + CTimer m_inactivityTimer; + CTimer m_pollInactivityTimer; + CTimer m_pollTimer; + CTimer m_waitTimer; + CTimer m_tryTimer; + unsigned int m_tryCount; + unsigned int m_id; + unsigned int m_seqNo; + time_t m_time; + DIRECTION m_direction; + wxString m_yourCall; + wxString m_myCall1; + wxString m_myCall2; + wxString m_rptCall1; + + void process(CAMBEData& header); + void process(CPollData& data); + void process(CConnectData& connect); + void process(CCCSData& data); + + unsigned int calcBackoff(); + + static void addToCache(const wxString& dtmf, const wxString& callsign); + static wxString findInCache(const wxString& dtmf); +}; + +#endif diff --git a/Common/CCSProtocolHandler.cpp b/Common/CCSProtocolHandler.cpp new file mode 100644 index 0000000..9c22456 --- /dev/null +++ b/Common/CCSProtocolHandler.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2013 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 "CCSProtocolHandler.h" + +#include "DStarDefines.h" +#include "Utils.h" + +// #define DUMP_TX + +const unsigned int BUFFER_LENGTH = 2000U; + +CCCSProtocolHandler::CCCSProtocolHandler(unsigned int port, const wxString& addr) : +m_socket(addr, port), +m_type(CT_NONE), +m_buffer(NULL), +m_length(0U), +m_yourAddress(), +m_yourPort(0U), +m_myPort(port) +{ + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CCCSProtocolHandler::~CCCSProtocolHandler() +{ + delete[] m_buffer; +} + +bool CCCSProtocolHandler::open() +{ + return m_socket.open(); +} + +unsigned int CCCSProtocolHandler::getPort() const +{ + return m_myPort; +} + +bool CCCSProtocolHandler::writeData(const CAMBEData& data) +{ + unsigned char buffer[100U]; + unsigned int length = data.getCCSData(buffer, 100U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Data"), buffer, length); +#endif + + return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort()); +} + +bool CCCSProtocolHandler::writePoll(const CPollData& poll) +{ + unsigned char buffer[30U]; + unsigned int length = poll.getCCSData(buffer, 30U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Poll"), buffer, length); +#endif + + return m_socket.write(buffer, length, poll.getYourAddress(), poll.getYourPort()); +} + +bool CCCSProtocolHandler::writeHeard(const CHeardData& heard) +{ + unsigned char buffer[100U]; + unsigned int length = heard.getCCSData(buffer, 100U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Heard"), buffer, length); +#endif + + return m_socket.write(buffer, length, heard.getAddress(), heard.getPort()); +} + +bool CCCSProtocolHandler::writeConnect(const CConnectData& connect) +{ + unsigned char buffer[40U]; + unsigned int length = connect.getCCSData(buffer, 40U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Connect"), buffer, length); +#endif + + return m_socket.write(buffer, length, connect.getYourAddress(), connect.getYourPort()); +} + +bool CCCSProtocolHandler::writeMisc(const CCCSData& data) +{ + unsigned char buffer[140U]; + unsigned int length = data.getCCSData(buffer, 140U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Misc"), buffer, length); +#endif + + return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort()); +} + +CCS_TYPE CCCSProtocolHandler::read() +{ + bool res = true; + + // Loop until we have no more data from the socket or we have data for the higher layers + while (res) + res = readPackets(); + + return m_type; +} + +bool CCCSProtocolHandler::readPackets() +{ + m_type = CT_NONE; + + // No more data? + int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_yourAddress, m_yourPort); + if (length <= 0) + return false; + + m_length = length; + + if (m_buffer[0] == '0' && m_buffer[1] == '0' && m_buffer[2] == '0' && m_buffer[3] == '1') { + m_type = CT_DATA; + return false; + } else if (m_buffer[0] == 'L' && m_buffer[1] == 'L' && m_buffer[2] == 'L') { + return true; + } else { + switch (m_length) { + case 14U: + m_type = CT_CONNECT; + return false; + case 25U: + m_type = CT_POLL; + return false; + case 100U: + case 20U: + case 17U: + m_type = CT_MISC; + return false; + case 39U: + return true; + default: + break; + } + } + + // An unknown type + CUtils::dump(wxT("Unknown packet type from CCS"), m_buffer, m_length); + + return true; +} + +CAMBEData* CCCSProtocolHandler::readData() +{ + if (m_type != CT_DATA) + return NULL; + + CAMBEData* data = new CAMBEData; + + bool res = data->setCCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete data; + return NULL; + } + + return data; +} + +CConnectData* CCCSProtocolHandler::readConnect() +{ + if (m_type != CT_CONNECT) + return NULL; + + CConnectData* connect = new CConnectData; + + bool res = connect->setCCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete connect; + return NULL; + } + + return connect; +} + +CPollData* CCCSProtocolHandler::readPoll() +{ + if (m_type != CT_POLL) + return NULL; + + CPollData* poll = new CPollData; + + bool res = poll->setCCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete poll; + return NULL; + } + + return poll; +} + +CCCSData* CCCSProtocolHandler::readMisc() +{ + if (m_type != CT_MISC) + return NULL; + + CCCSData* data = new CCCSData; + + bool res = data->setCCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete data; + return NULL; + } + + return data; +} + +void CCCSProtocolHandler::close() +{ + m_socket.close(); +} diff --git a/Common/CCSProtocolHandler.h b/Common/CCSProtocolHandler.h new file mode 100644 index 0000000..87bc9f1 --- /dev/null +++ b/Common/CCSProtocolHandler.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef CCSProtocolHandler_H +#define CCSProtocolHandler_H + +#include "UDPReaderWriter.h" +#include "DStarDefines.h" +#include "ConnectData.h" +#include "HeardData.h" +#include "AMBEData.h" +#include "PollData.h" +#include "CCSData.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include + +enum CCS_TYPE { + CT_NONE, + CT_DATA, + CT_POLL, + CT_CONNECT, + CT_MISC +}; + +class CCCSProtocolHandler { +public: + CCCSProtocolHandler(unsigned int port, const wxString& addr = wxEmptyString); + ~CCCSProtocolHandler(); + + bool open(); + + unsigned int getPort() const; + + bool writeData(const CAMBEData& data); + bool writeConnect(const CConnectData& connect); + bool writePoll(const CPollData& poll); + bool writeHeard(const CHeardData& heard); + bool writeMisc(const CCCSData& data); + + CCS_TYPE read(); + CAMBEData* readData(); + CPollData* readPoll(); + CConnectData* readConnect(); + CCCSData* readMisc(); + + void close(); + +private: + CUDPReaderWriter m_socket; + CCS_TYPE m_type; + unsigned char* m_buffer; + unsigned int m_length; + in_addr m_yourAddress; + unsigned int m_yourPort; + unsigned int m_myPort; + + bool readPackets(); +}; + +#endif diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 10fcc19..bdbcfc3 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -156,6 +156,9 @@ + + + @@ -221,6 +224,10 @@ + + + + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index a42af77..8365240 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -41,6 +41,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + Source Files @@ -232,6 +241,18 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + Header Files diff --git a/Common/ConnectData.cpp b/Common/ConnectData.cpp index 73f128a..2489b73 100644 --- a/Common/ConnectData.cpp +++ b/Common/ConnectData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010,2012,2013,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2010,2012,2013 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 @@ -203,6 +203,33 @@ bool CConnectData::setDCSData(const unsigned char* data, unsigned int length, co return true; } +bool CConnectData::setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 14U); + wxASSERT(yourPort > 0U); + + m_repeater = wxString((const char*)data, wxConvLocal, LONG_CALLSIGN_LENGTH); + m_repeater.SetChar(LONG_CALLSIGN_LENGTH - 1U, data[LONG_CALLSIGN_LENGTH + 0U]); + + if (data[LONG_CALLSIGN_LENGTH + 2U] == 'A' && + data[LONG_CALLSIGN_LENGTH + 3U] == 'C' && + data[LONG_CALLSIGN_LENGTH + 4U] == 'K') + m_type = CT_ACK; + else if (data[LONG_CALLSIGN_LENGTH + 2U] == 'N' && + data[LONG_CALLSIGN_LENGTH + 3U] == 'A' && + data[LONG_CALLSIGN_LENGTH + 4U] == 'K') + m_type = CT_NAK; + else + return false; + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + + return true; +} + bool CConnectData::setDPlusData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) { wxASSERT(data != NULL); @@ -365,6 +392,46 @@ unsigned int CConnectData::getDCSData(unsigned char *data, unsigned int length) } } +unsigned int CConnectData::getCCSData(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 39U); + + ::memset(data, ' ', 39U); + + for (unsigned int i = 0U; i < m_repeater.Len() && i < (LONG_CALLSIGN_LENGTH - 1U); i++) + data[i] = m_repeater.GetChar(i); + + data[LONG_CALLSIGN_LENGTH + 0U] = m_repeater.GetChar(LONG_CALLSIGN_LENGTH - 1U); + + switch (m_type) { + case CT_LINK1: + case CT_LINK2: { + data[9U] = 0x41U; + data[10U] = '@'; + + for (unsigned int i = 0U; i < m_locator.Len(); i++) + data[11U + i] = m_locator.GetChar(i); + + data[17U] = 0x20U; + data[18U] = '@'; + + wxString text; + text.Printf(wxT("ircDDB_GW-%s"), VERSION.Left(8U).c_str()); + + for (unsigned int i = 0U; i < text.Len(); i++) + data[19U + i] = text.GetChar(i); + } + return 39U; + + case CT_UNLINK: + return 19U; + + default: + return 0U; + } +} + unsigned int CConnectData::getDPlusData(unsigned char *data, unsigned int length) const { wxASSERT(data != NULL); diff --git a/Common/ConnectData.h b/Common/ConnectData.h index f3cb287..da6ea78 100644 --- a/Common/ConnectData.h +++ b/Common/ConnectData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010,2012,2013,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2010,2012,2013 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 @@ -50,10 +50,12 @@ public: bool setDExtraData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); bool setDPlusData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); bool setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); + bool setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); unsigned int getDExtraData(unsigned char* data, unsigned int length) const; unsigned int getDPlusData(unsigned char* data, unsigned int length) const; unsigned int getDCSData(unsigned char* data, unsigned int length) const; + unsigned int getCCSData(unsigned char* data, unsigned int length) const; wxString getRepeater() const; wxString getReflector() const; diff --git a/Common/DStarDefines.h b/Common/DStarDefines.h index a2dc448..8ea9ae6 100644 --- a/Common/DStarDefines.h +++ b/Common/DStarDefines.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2015,2018 by Jonathan Naylor, G4KLX + * Copyright (C) 2009-2015 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 @@ -112,10 +112,11 @@ const unsigned char SCRAMBLER_BYTE3 = 0x93U; const unsigned int DPLUS_PORT = 20001U; const unsigned int DEXTRA_PORT = 30001U; const unsigned int DCS_PORT = 30051U; +const unsigned int CCS_PORT = 30062U; // Port for CCS7 const unsigned int G2_DV_PORT = 40000U; const unsigned int G2_DD_PORT = 40001U; -const unsigned int NETWORK_TIMEOUT = 2U; // Network timeout for G2, DCS, DExtra, and D-Plus +const unsigned int NETWORK_TIMEOUT = 2U; // Network timeout for G2, CCS, DCS, DExtra, and D-Plus const unsigned int REPEATER_TIMEOUT = 2U; // Repeater timeout const unsigned int REPLY_TIME = 2U; // The turnaround time for version, echo, audio prompts @@ -137,7 +138,8 @@ enum AUDIO_SOURCE { AS_DEXTRA, AS_DCS, AS_DUP, - AS_VERSION + AS_VERSION, + AS_CCS }; enum DSTAR_RX_STATE { diff --git a/Common/DTMF.cpp b/Common/DTMF.cpp index 05ca390..a05cbed 100644 --- a/Common/DTMF.cpp +++ b/Common/DTMF.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012,2013,2015,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2012,2013,2015 by Jonathan Naylor G4KLX * Copyright (C) 2011 by DV Developer Group. DJ0ABR * * This program is free software; you can redistribute it and/or modify @@ -167,6 +167,8 @@ wxString CDTMF::translate() return processReflector(wxT("XRF"), command.Mid(1U)); else if (command.GetChar(0U) == wxT('D')) return processReflector(wxT("DCS"), command.Mid(1U)); + else + return processCCS(command); } void CDTMF::reset() @@ -219,3 +221,97 @@ wxString CDTMF::processReflector(const wxString& prefix, const wxString& command return out; } } + +wxString CDTMF::processCCS(const wxString& command) const +{ + unsigned int len = command.Len(); + + wxString out = wxEmptyString; + + switch (len) { + case 3U: { + // CCS7 for local repeater without band + unsigned long n; + command.ToULong(&n); + if (n == 0UL) + return wxEmptyString; + out.Printf(wxT("C%03lu "), n); + } + break; + case 4U: { + wxChar c = command.GetChar(3U); + if (c == wxT('A') || c == wxT('B') || c == wxT('C') || c == wxT('D')) { + // CCS7 for local repeater with band + unsigned long n; + command.Mid(0U, 3U).ToULong(&n); + if (n == 0UL) + return wxEmptyString; + out.Printf(wxT("C%03lu%c "), n, c); + } else { + // CCS7 for local user + unsigned long n; + command.ToULong(&n); + if (n == 0UL) + return wxEmptyString; + out.Printf(wxT("C%04lu "), n); + } + } + break; + case 5U: { + wxChar c = command.GetChar(4U); + if (c == wxT('A') || c == wxT('B') || c == wxT('C') || c == wxT('D')) { + // CCS7 for local hostspot with band + unsigned long n; + command.Mid(0U, 4U).ToULong(&n); + if (n == 0UL) + return wxEmptyString; + out.Printf(wxT("C%04lu%c "), n, c); + } + } + break; + case 6U: { + // CCS7 for full repeater without band + unsigned long n; + command.ToULong(&n); + if (n == 0UL) + return wxEmptyString; + out.Printf(wxT("C%06lu "), n); + } + break; + case 7U: { + wxChar c = command.GetChar(6U); + if (c == wxT('A') || c == wxT('B') || c == wxT('C') || c == wxT('D')) { + // CCS7 for full repeater with band + unsigned long n; + command.Mid(0U, 6U).ToULong(&n); + if (n == 0UL) + return wxEmptyString; + out.Printf(wxT("C%06lu%c"), n, c); + } else { + // CCS7 for full user or CCS7 for full hostpot without band + unsigned long n; + command.ToULong(&n); + if (n == 0UL) + return wxEmptyString; + out.Printf(wxT("C%07lu"), n); + } + } + break; + case 8U: { + wxChar c = command.GetChar(7U); + if (c == wxT('A') || c == wxT('B') || c == wxT('C') || c == wxT('D')) { + // CCS7 for full hotspot with band + unsigned long n; + command.Mid(0U, 7U).ToULong(&n); + if (n == 0UL) + return wxEmptyString; + out.Printf(wxT("C%07lu%c"), n, c); + } + } + break; + default: + break; + } + + return out; +} diff --git a/Common/DTMF.h b/Common/DTMF.h index 09e60a8..71ab364 100644 --- a/Common/DTMF.h +++ b/Common/DTMF.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012,2013,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2012,2013 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 @@ -43,6 +43,7 @@ private: wxChar m_lastChar; wxString processReflector(const wxString& prefix, const wxString& command) const; + wxString processCCS(const wxString& command) const; }; #endif diff --git a/Common/Defs.h b/Common/Defs.h index 8422954..f2c5c31 100644 --- a/Common/Defs.h +++ b/Common/Defs.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2015,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2010-2015 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 @@ -24,6 +24,7 @@ const wxString DEXTRA_HOSTS_FILE_NAME = wxT("DExtra_Hosts.txt"); const wxString DPLUS_HOSTS_FILE_NAME = wxT("DPlus_Hosts.txt"); const wxString DCS_HOSTS_FILE_NAME = wxT("DCS_Hosts.txt"); +const wxString CCS_HOSTS_FILE_NAME = wxT("CCS_Hosts.txt"); const wxString GATEWAY_HOSTS_FILE_NAME = wxT("Gateway_Hosts.txt"); const wxString LINKS_BASE_NAME = wxT("Links"); @@ -56,7 +57,8 @@ enum DIRECTION { enum PROTOCOL { PROTO_DEXTRA, PROTO_DPLUS, - PROTO_DCS + PROTO_DCS, + PROTO_CCS }; enum HW_TYPE { @@ -107,10 +109,12 @@ enum LINK_STATUS { LS_LINKING_DEXTRA, LS_LINKING_DPLUS, LS_LINKING_DCS, + LS_LINKING_CCS, LS_LINKED_LOOPBACK, LS_LINKED_DEXTRA, LS_LINKED_DPLUS, - LS_LINKED_DCS + LS_LINKED_DCS, + LS_LINKED_CCS }; enum SLOWDATA_STATE { diff --git a/Common/HeaderData.cpp b/Common/HeaderData.cpp index dd49ba3..fd518bd 100644 --- a/Common/HeaderData.cpp +++ b/Common/HeaderData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2010-2014 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 @@ -256,6 +256,24 @@ void CHeaderData::setDCSData(const unsigned char *data, unsigned int length, con m_myPort = myPort; } +void CHeaderData::setCCSData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 100U); + + m_id = data[44U] * 256U + data[43U]; + + ::memcpy(m_rptCall2, data + 7U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_rptCall1, data + 15U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_yourCall, data + 23U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall1, data + 31U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall2, data + 39U, SHORT_CALLSIGN_LENGTH); + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; +} + bool CHeaderData::setG2Data(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort) { wxASSERT(data != NULL); @@ -510,6 +528,18 @@ void CHeaderData::getDCSData(unsigned char *data, unsigned int length) const ::memcpy(data + 39U, m_myCall2, SHORT_CALLSIGN_LENGTH); } +void CHeaderData::getCCSData(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 100U); + + ::memcpy(data + 7U, m_rptCall2, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 15U, m_rptCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 23U, m_yourCall, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 31U, m_myCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 39U, m_myCall2, SHORT_CALLSIGN_LENGTH); +} + unsigned int CHeaderData::getG2Data(unsigned char *data, unsigned int length, bool check) const { wxASSERT(data != NULL); diff --git a/Common/HeaderData.h b/Common/HeaderData.h index ba21b57..791ec36 100644 --- a/Common/HeaderData.h +++ b/Common/HeaderData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2010-2014 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 @@ -42,6 +42,7 @@ public: bool setDExtraData(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); bool setDPlusData(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); void setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); + void setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); unsigned int getIcomRepeaterData(unsigned char* data, unsigned int length, bool check) const; unsigned int getHBRepeaterData(unsigned char* data, unsigned int length, bool check) const; @@ -49,6 +50,7 @@ public: unsigned int getDPlusData(unsigned char* data, unsigned int length, bool check) const; unsigned int getG2Data(unsigned char* data, unsigned int length, bool check) const; void getDCSData(unsigned char* data, unsigned int length) const; + void getCCSData(unsigned char* data, unsigned int length) const; bool setDVTOOLData(const unsigned char* data, unsigned int length, bool check); diff --git a/Common/HeardData.cpp b/Common/HeardData.cpp index 9e97dbc..e2d8f2e 100644 --- a/Common/HeardData.cpp +++ b/Common/HeardData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012,2013,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2012,2013 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 @@ -68,6 +68,45 @@ bool CHeardData::setIcomRepeaterData(const unsigned char *data, unsigned int len return true; } +unsigned int CHeardData::getCCSData(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 100U); + + ::memset(data, 0x00U, 100U); + + data[0U] = '0'; + data[1U] = '0'; + data[2U] = '0'; + data[3U] = '1'; + + ::memset(data + 7U, ' ', 36U); + + for (unsigned int i = 0U; i < m_reflector.Len(); i++) + data[i + 7U] = m_reflector.GetChar(i); + + for (unsigned int i = 0U; i < m_repeater.Len(); i++) + data[i + 15U] = m_repeater.GetChar(i); + + ::memcpy(data + 23U, "CQCQCQ ", LONG_CALLSIGN_LENGTH); + + for (unsigned int i = 0U; i < m_user.Len(); i++) + data[i + 31U] = m_user.GetChar(i); + + for (unsigned int i = 0U; i < m_ext.Len(); i++) + data[i + 39U] = m_ext.GetChar(i); + + data[61U] = 0x01U; + + data[63U] = 0x21U; + + ::memset(data + 64U, ' ', 20U); + + data[93U] = 0x36U; + + return 100U; +} + wxString CHeardData::getRepeater() const { return m_repeater; diff --git a/Common/HeardData.h b/Common/HeardData.h index e1b2eed..61010fa 100644 --- a/Common/HeardData.h +++ b/Common/HeardData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012,2013,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2012,2013 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 @@ -39,6 +39,8 @@ public: bool setIcomRepeaterData(const unsigned char* data, unsigned int length, const in_addr& address, unsigned int port); + unsigned int getCCSData(unsigned char* data, unsigned int length) const; + wxString getRepeater() const; wxString getUser() const; diff --git a/Common/IRCDDBGatewayConfig.cpp b/Common/IRCDDBGatewayConfig.cpp index 7062737..7e3da47 100644 --- a/Common/IRCDDBGatewayConfig.cpp +++ b/Common/IRCDDBGatewayConfig.cpp @@ -138,6 +138,8 @@ const wxString KEY_DPLUS_ENABLED = wxT("dplusEnabled"); const wxString KEY_DPLUS_MAXDONGLES = wxT("dplusMaxDongles"); const wxString KEY_DPLUS_LOGIN = wxT("dplusLogin"); const wxString KEY_DCS_ENABLED = wxT("dcsEnabled"); +const wxString KEY_CCS_ENABLED = wxT("ccsEnabled"); +const wxString KEY_CCS_HOST = wxT("ccsHost"); const wxString KEY_XLX_ENABLED = wxT("xlxEnabled"); const wxString KEY_XLX_OVERRIDE_LOCAL = wxT("xlxOverrideLocal"); const wxString KEY_XLX_HOSTS_FILE_URL = wxT("xlxHostsFileUrl"); @@ -259,6 +261,8 @@ const bool DEFAULT_DPLUS_ENABLED = false; const unsigned int DEFAULT_DPLUS_MAXDONGLES = 5U; const wxString DEFAULT_DPLUS_LOGIN = wxEmptyString; const bool DEFAULT_DCS_ENABLED = true; +const bool DEFAULT_CCS_ENABLED = true; +const wxString DEFAULT_CCS_HOST = wxT("CCS704 "); const bool DEFAULT_XLX_ENABLED = true; const bool DEFAULT_XLX_OVERRIDE_LOCAL = true; const wxString DEFAULT_XLX_HOSTS_FILE_URL = _T("http://xlxapi.rlx.lu/api.php?do=GetReflectorHostname"); @@ -409,6 +413,8 @@ m_dplusEnabled(DEFAULT_DPLUS_ENABLED), m_dplusMaxDongles(DEFAULT_DPLUS_MAXDONGLES), m_dplusLogin(DEFAULT_DPLUS_LOGIN), m_dcsEnabled(DEFAULT_DCS_ENABLED), +m_ccsEnabled(DEFAULT_CCS_ENABLED), +m_ccsHost(DEFAULT_CCS_HOST), m_xlxEnabled(DEFAULT_XLX_ENABLED), m_xlxOverrideLocal(DEFAULT_XLX_OVERRIDE_LOCAL), m_xlxHostsFileUrl(DEFAULT_XLX_HOSTS_FILE_URL), @@ -741,6 +747,10 @@ m_y(DEFAULT_WINDOW_Y) m_config->Read(m_name + KEY_DCS_ENABLED, &m_dcsEnabled, DEFAULT_DCS_ENABLED); + m_config->Read(m_name + KEY_CCS_ENABLED, &m_ccsEnabled, DEFAULT_CCS_ENABLED); + + m_config->Read(m_name + KEY_CCS_HOST, &m_ccsHost, DEFAULT_CCS_HOST); + m_config->Read(m_name + KEY_XLX_ENABLED, &m_xlxEnabled, DEFAULT_XLX_ENABLED); m_config->Read(m_name + KEY_XLX_OVERRIDE_LOCAL, &m_xlxOverrideLocal, DEFAULT_XLX_OVERRIDE_LOCAL); @@ -1016,6 +1026,8 @@ m_dplusEnabled(DEFAULT_DPLUS_ENABLED), m_dplusMaxDongles(DEFAULT_DPLUS_MAXDONGLES), m_dplusLogin(DEFAULT_DPLUS_LOGIN), m_dcsEnabled(DEFAULT_DCS_ENABLED), +m_ccsEnabled(DEFAULT_CCS_ENABLED), +m_ccsHost(DEFAULT_CCS_HOST), m_xlxEnabled(DEFAULT_XLX_ENABLED), m_xlxOverrideLocal(DEFAULT_XLX_OVERRIDE_LOCAL), m_xlxHostsFileUrl(DEFAULT_XLX_HOSTS_FILE_URL), @@ -1399,6 +1411,11 @@ m_y(DEFAULT_WINDOW_Y) } else if (key.IsSameAs(KEY_DCS_ENABLED)) { val.ToLong(&temp1); m_dcsEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_CCS_ENABLED)) { + val.ToLong(&temp1); + m_ccsEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_CCS_HOST)) { + m_ccsHost = val; } else if (key.IsSameAs(KEY_XLX_ENABLED)) { val.ToLong(&temp1); m_xlxEnabled = temp1 == 1L; @@ -1910,14 +1927,18 @@ void CIRCDDBGatewayConfig::setDPlus(bool enabled, unsigned int maxDongles, const m_dplusLogin = login; } -void CIRCDDBGatewayConfig::getDCS(bool& dcsEnabled) const +void CIRCDDBGatewayConfig::getDCS(bool& dcsEnabled, bool& ccsEnabled, wxString& ccsHost) const { dcsEnabled = m_dcsEnabled; + ccsEnabled = m_ccsEnabled; + ccsHost = m_ccsHost; } -void CIRCDDBGatewayConfig::setDCS(bool dcsEnabled) +void CIRCDDBGatewayConfig::setDCS(bool dcsEnabled, bool ccsEnabled, const wxString& ccsHost) { m_dcsEnabled = dcsEnabled; + m_ccsEnabled = ccsEnabled; + m_ccsHost = ccsHost; } void CIRCDDBGatewayConfig::getXLX(bool& xlxEnabled, bool& xlxOverrideLocal, wxString& xlxHostsFileUrl) @@ -2354,6 +2375,8 @@ bool CIRCDDBGatewayConfig::write() m_config->Write(m_name + KEY_DPLUS_MAXDONGLES, long(m_dplusMaxDongles)); m_config->Write(m_name + KEY_DPLUS_LOGIN, m_dplusLogin); m_config->Write(m_name + KEY_DCS_ENABLED, m_dcsEnabled); + m_config->Write(m_name + KEY_CCS_ENABLED, m_ccsEnabled); + m_config->Write(m_name + KEY_CCS_HOST, m_ccsHost); m_config->Write(m_name + KEY_XLX_ENABLED, m_xlxEnabled); m_config->Write(m_name + KEY_XLX_OVERRIDE_LOCAL, m_xlxOverrideLocal); m_config->Write(m_name + KEY_XLX_HOSTS_FILE_URL, m_xlxHostsFileUrl); @@ -2560,6 +2583,8 @@ bool CIRCDDBGatewayConfig::write() buffer.Printf(wxT("%s=%u"), KEY_DPLUS_MAXDONGLES.c_str(), m_dplusMaxDongles); file.AddLine(buffer); buffer.Printf(wxT("%s=%s"), KEY_DPLUS_LOGIN.c_str(), m_dplusLogin.c_str()); file.AddLine(buffer); buffer.Printf(wxT("%s=%d"), KEY_DCS_ENABLED.c_str(), m_dcsEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_CCS_ENABLED.c_str(), m_ccsEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_CCS_HOST.c_str(), m_ccsHost.c_str()); file.AddLine(buffer); buffer.Printf(wxT("%s=%d"), KEY_XLX_ENABLED.c_str(), m_xlxEnabled ? 1 : 0); file.AddLine(buffer); buffer.Printf(wxT("%s=%d"), KEY_XLX_OVERRIDE_LOCAL.c_str(), m_xlxOverrideLocal ? 1 : 0); file.AddLine(buffer); buffer.Printf(wxT("%s=%s"), KEY_XLX_HOSTS_FILE_URL.c_str(), m_xlxHostsFileUrl.c_str()); file.AddLine(buffer); diff --git a/Common/IRCDDBGatewayConfig.h b/Common/IRCDDBGatewayConfig.h index 24f3c53..5583b91 100644 --- a/Common/IRCDDBGatewayConfig.h +++ b/Common/IRCDDBGatewayConfig.h @@ -68,8 +68,8 @@ public: void getDPlus(bool& enabled, unsigned int& maxDongles, wxString& login) const; void setDPlus(bool enabled, unsigned int maxDongles, const wxString& login); - void getDCS(bool& dcsEnabled) const; - void setDCS(bool dcsEnabled); + void getDCS(bool& dcsEnabled, bool& ccsEnabled, wxString& ccsHost) const; + void setDCS(bool dcsEnabled, bool ccsEnabled, const wxString& ccsHost); void getXLX(bool& xlxEnabled, bool& xlxOverrideLocal, wxString& xlxHostsFileUrl); void setXLX(bool xlxEnabled, bool xlxOverrideLocal, wxString xlxHostsFileUrl); @@ -241,6 +241,8 @@ private: unsigned int m_dplusMaxDongles; wxString m_dplusLogin; bool m_dcsEnabled; + bool m_ccsEnabled; + wxString m_ccsHost; bool m_xlxEnabled; bool m_xlxOverrideLocal; wxString m_xlxHostsFileUrl; diff --git a/Common/Makefile b/Common/Makefile index 9e569ad..8643148 100644 --- a/Common/Makefile +++ b/Common/Makefile @@ -1,5 +1,5 @@ OBJECTS = AMBEData.o AnnouncementUnit.o APRSCollector.o APRSWriter.o APRSWriterThread.o AudioUnit.o CacheManager.o CallsignList.o \ - CallsignServer.o CCITTChecksum.o ConnectData.o DCSHandler.o DCSProtocolHandler.o \ + CallsignServer.o CCITTChecksum.o CCSData.o CCSHandler.o CCSProtocolHandler.o ConnectData.o DCSHandler.o DCSProtocolHandler.o \ DCSProtocolHandlerPool.o DDData.o DDHandler.o DExtraHandler.o DExtraProtocolHandler.o DExtraProtocolHandlerPool.o \ DPlusAuthenticator.o DPlusHandler.o DPlusProtocolHandler.o DPlusProtocolHandlerPool.o DRATSServer.o DTMF.o \ DummyRepeaterProtocolHandler.o DVTOOLFileReader.o EchoUnit.o G2Handler.o G2ProtocolHandler.o GatewayCache.o \ diff --git a/Common/PollData.cpp b/Common/PollData.cpp index 52123af..04b2569 100644 --- a/Common/PollData.cpp +++ b/Common/PollData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010,2012,2013,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2010,2012,2013 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 @@ -125,6 +125,21 @@ bool CPollData::setDCSData(const unsigned char* data, unsigned int length, const return true; } +bool CPollData::setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 25U); + wxASSERT(yourPort > 0U); + + m_data1 = wxString((const char*)(data + 0U), wxConvLocal, 25U); + m_length = length; + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + + return true; +} + bool CPollData::setDPlusData(const unsigned char*, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) { wxASSERT(yourPort > 0U); @@ -188,6 +203,24 @@ unsigned int CPollData::getDCSData(unsigned char *data, unsigned int length) con } } +unsigned int CPollData::getCCSData(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 25U); + + ::memset(data, ' ', 25U); + + for (unsigned int i = 0U; i < m_data1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + data[i + 0U] = m_data1.GetChar(i); + + if (!m_data2.IsEmpty()) { + for (unsigned int i = 0U; i < m_data2.Len() && i < LONG_CALLSIGN_LENGTH; i++) + data[i + 8U] = m_data2.GetChar(i); + } + + return 25U; +} + unsigned int CPollData::getDPlusData(unsigned char *data, unsigned int length) const { wxASSERT(data != NULL); diff --git a/Common/PollData.h b/Common/PollData.h index 5e9822f..d85ec78 100644 --- a/Common/PollData.h +++ b/Common/PollData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010,2012,2013,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2010,2012,2013 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 @@ -40,10 +40,12 @@ public: bool setDExtraData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); bool setDPlusData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); bool setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); + bool setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort); unsigned int getDExtraData(unsigned char* data, unsigned int length) const; unsigned int getDPlusData(unsigned char* data, unsigned int length) const; unsigned int getDCSData(unsigned char* data, unsigned int length) const; + unsigned int getCCSData(unsigned char* data, unsigned int length) const; wxString getData1() const; void setData1(const wxString& data); diff --git a/Common/RemoteHandler.cpp b/Common/RemoteHandler.cpp index e0cfee9..dbbe1fd 100644 --- a/Common/RemoteHandler.cpp +++ b/Common/RemoteHandler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011,2012,2013,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2011,2012,2013 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 @@ -148,6 +148,7 @@ void CRemoteHandler::sendRepeater(const wxString& callsign) CDExtraHandler::getInfo(repeater, *data); CDPlusHandler::getInfo(repeater, *data); CDCSHandler::getInfo(repeater, *data); + CCCSHandler::getInfo(repeater, *data); m_handler.sendRepeater(*data); } diff --git a/Common/RepeaterHandler.cpp b/Common/RepeaterHandler.cpp index 5548fc3..215e0dd 100644 --- a/Common/RepeaterHandler.cpp +++ b/Common/RepeaterHandler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2015,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2010-2015 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 @@ -21,6 +21,7 @@ #include "DPlusHandler.h" #include "DStarDefines.h" #include "DCSHandler.h" +#include "CCSHandler.h" #include "HeaderData.h" #include "DDHandler.h" #include "AMBEData.h" @@ -126,6 +127,7 @@ m_version(NULL), m_drats(NULL), m_dtmf(), m_pollTimer(1000U, 900U), // 15 minutes +m_ccsHandler(NULL), m_lastReflector(), m_heardUser(), m_heardRepeater(), @@ -565,6 +567,10 @@ void CRepeaterHandler::processRepeater(CHeaderData& header) if (!m_heardUser.IsEmpty() && !m_myCall1.IsSameAs(m_heardUser) && m_irc != NULL) m_irc->sendHeard(m_heardUser, wxT(" "), wxT(" "), m_heardRepeater, wxT(" "), 0x00U, 0x00U, 0x00U); + // Inform CCS + m_ccsHandler->writeHeard(header); + m_ccsHandler->writeHeader(header); + // The Icom heard timer m_heardTimer.stop(); @@ -705,11 +711,16 @@ void CRepeaterHandler::processRepeater(CHeaderData& header) return; } - g2CommandHandler(m_yourCall, m_myCall1, header); - - if (m_g2Status == G2_NONE) { - reflectorCommandHandler(m_yourCall, m_myCall1, wxT("UR Call")); + if (isCCSCommand(m_yourCall)) { + ccsCommandHandler(m_yourCall, m_myCall1, wxT("UR Call")); sendToOutgoing(header); + } else { + g2CommandHandler(m_yourCall, m_myCall1, header); + + if (m_g2Status == G2_NONE) { + reflectorCommandHandler(m_yourCall, m_myCall1, wxT("UR Call")); + sendToOutgoing(header); + } } } @@ -745,6 +756,8 @@ void CRepeaterHandler::processRepeater(CAMBEData& data) if (!m_restricted && m_yourCall.Left(4U).IsSameAs(wxT("CQCQ"))) { if (command.IsEmpty()) { // Do nothing + } else if (isCCSCommand(command)) { + ccsCommandHandler(command, m_myCall1, wxT("DTMF")); } else if (command.IsSameAs(wxT(" I"))) { m_infoNeeded = true; } else { @@ -757,6 +770,9 @@ void CRepeaterHandler::processRepeater(CAMBEData& data) // Incoming links get everything sendToIncoming(data); + // CCS gets everything + m_ccsHandler->writeAMBE(data); + if (m_drats != NULL) m_drats->writeData(data); @@ -941,7 +957,10 @@ void CRepeaterHandler::processBusy(CHeaderData& header) if (m_yourCall.Left(4).IsSameAs(wxT("CQCQ")) || m_yourCall.IsSameAs(wxT(" E")) || m_yourCall.IsSameAs(wxT(" I"))) return; - reflectorCommandHandler(m_yourCall, m_myCall1, wxT("background UR Call")); + if (isCCSCommand(m_yourCall)) + ccsCommandHandler(m_yourCall, m_myCall1, wxT("background UR Call")); + else + reflectorCommandHandler(m_yourCall, m_myCall1, wxT("background UR Call")); } void CRepeaterHandler::processBusy(CAMBEData& data) @@ -963,6 +982,8 @@ void CRepeaterHandler::processBusy(CAMBEData& data) if (!m_restricted && m_yourCall.Left(4U).IsSameAs(wxT("CQCQ"))) { if (command.IsEmpty()) { // Do nothing + } else if (isCCSCommand(command)) { + ccsCommandHandler(command, m_myCall1, wxT("background DTMF")); } else if (command.IsSameAs(wxT(" I"))) { // Do nothing } else { @@ -1096,6 +1117,9 @@ bool CRepeaterHandler::process(CHeaderData& header, DIRECTION, AUDIO_SOURCE sour sendToIncoming(header); + if (source == AS_DPLUS || source == AS_DEXTRA || source == AS_DCS) + m_ccsHandler->writeHeader(header); + if (source == AS_G2 || source == AS_INFO || source == AS_VERSION || source == AS_XBAND || source == AS_ECHO) return true; @@ -1130,6 +1154,9 @@ bool CRepeaterHandler::process(CAMBEData& data, DIRECTION, AUDIO_SOURCE source) sendToIncoming(data); + if (source == AS_DPLUS || source == AS_DEXTRA || source == AS_DCS) + m_ccsHandler->writeAMBE(data); + if (source == AS_G2 || source == AS_INFO || source == AS_VERSION || source == AS_XBAND || source == AS_ECHO) return true; @@ -1428,6 +1455,16 @@ void CRepeaterHandler::clockInt(unsigned int ms) writeNotLinked(); triggerInfo(); + } else if (m_linkStatus == LS_LINKING_CCS) { + // CCS didn't reply in time + wxLogMessage(wxT("CCS did not reply within five seconds")); + + m_ccsHandler->stopLink(); + + m_linkStatus = LS_NONE; + m_linkRepeater.Clear(); + + restoreLinks(); } } @@ -1708,6 +1745,17 @@ void CRepeaterHandler::linkRefused(DSTAR_PROTOCOL protocol, const wxString& call void CRepeaterHandler::link(RECONNECT reconnect, const wxString& reflector) { + // CCS removal + if (m_linkStatus == LS_LINKING_CCS || m_linkStatus == LS_LINKED_CCS) { + wxLogMessage(wxT("Dropping CCS link to %s"), m_linkRepeater.c_str()); + + m_ccsHandler->stopLink(); + + m_linkStatus = LS_NONE; + m_linkRepeater.Clear(); + m_queryTimer.stop(); + } + m_linkStartup = reflector; m_linkReconnect = reconnect; @@ -1844,6 +1892,11 @@ void CRepeaterHandler::link(RECONNECT reconnect, const wxString& reflector) void CRepeaterHandler::unlink(PROTOCOL protocol, const wxString& reflector) { + if (protocol == PROTO_CCS) { + m_ccsHandler->unlink(reflector); + return; + } + if (m_linkReconnect == RECONNECT_FIXED && m_linkRepeater.IsSameAs(reflector)) { wxLogMessage(wxT("Cannot unlink %s because it is fixed"), reflector.c_str()); return; @@ -1869,6 +1922,9 @@ void CRepeaterHandler::unlink(PROTOCOL protocol, const wxString& reflector) void CRepeaterHandler::g2CommandHandler(const wxString& callsign, const wxString& user, CHeaderData& header) { + if (m_linkStatus == LS_LINKING_CCS || m_linkStatus == LS_LINKED_CCS) + return; + if (callsign.Left(1).IsSameAs(wxT("/"))) { if (m_irc == NULL) { wxLogMessage(wxT("%s is trying to G2 route with ircDDB disabled"), user.c_str()); @@ -1961,8 +2017,27 @@ void CRepeaterHandler::g2CommandHandler(const wxString& callsign, const wxString } } +void CRepeaterHandler::ccsCommandHandler(const wxString& callsign, const wxString& user, const wxString& type) +{ + if (callsign.IsSameAs(wxT("CA "))) { + m_ccsHandler->stopLink(user, type); + } else { + CCS_STATUS status = m_ccsHandler->getStatus(); + if (status == CS_CONNECTED) { + suspendLinks(); + m_queryTimer.start(); + m_linkStatus = LS_LINKING_CCS; + m_linkRepeater = callsign.Mid(1U); + m_ccsHandler->startLink(m_linkRepeater, user, type); + } + } +} + void CRepeaterHandler::reflectorCommandHandler(const wxString& callsign, const wxString& user, const wxString& type) { + if (m_linkStatus == LS_LINKING_CCS || m_linkStatus == LS_LINKED_CCS) + return; + if (m_linkReconnect == RECONNECT_FIXED) return; @@ -2249,6 +2324,12 @@ void CRepeaterHandler::startupInt() } + m_ccsHandler = new CCCSHandler(this, m_rptCallsign, m_index + 1U, m_latitude, m_longitude, m_frequency, m_offset, m_description1, m_description2, m_url, CCS_PORT + m_index); + + // Start up our CCS link if we are DV mode + if (!m_ddMode) + m_ccsHandler->connect(); + // Link to a startup reflector/repeater if (m_linkAtStartup && !m_linkStartup.IsEmpty()) { wxLogMessage(wxT("Linking %s at startup to %s"), m_rptCallsign.c_str(), m_linkStartup.c_str()); @@ -2379,6 +2460,8 @@ void CRepeaterHandler::writeLinkingTo(const wxString &callsign) m_infoAudio->setStatus(m_linkStatus, m_linkRepeater, text); triggerInfo(); + + m_ccsHandler->setReflector(); } void CRepeaterHandler::writeLinkedTo(const wxString &callsign) @@ -2427,6 +2510,8 @@ void CRepeaterHandler::writeLinkedTo(const wxString &callsign) m_infoAudio->setStatus(m_linkStatus, m_linkRepeater, text); triggerInfo(); + + m_ccsHandler->setReflector(callsign); } void CRepeaterHandler::writeNotLinked() @@ -2475,6 +2560,8 @@ void CRepeaterHandler::writeNotLinked() m_infoAudio->setStatus(m_linkStatus, m_linkRepeater, text); triggerInfo(); + + m_ccsHandler->setReflector(); } void CRepeaterHandler::writeIsBusy(const wxString& callsign) @@ -2539,6 +2626,228 @@ void CRepeaterHandler::writeIsBusy(const wxString& callsign) m_infoAudio->setStatus(m_linkStatus, m_linkRepeater, text); m_infoAudio->setTempStatus(m_linkStatus, m_linkRepeater, tempText); triggerInfo(); + + m_ccsHandler->setReflector(); +} + +void CRepeaterHandler::ccsLinkMade(const wxString& callsign, DIRECTION direction) +{ + wxString text; + + switch (m_language) { + case TL_DEUTSCH: + text.Printf(wxT("Verlinkt zu %s"), callsign.c_str()); + break; + case TL_DANSK: + text.Printf(wxT("Linket til %s"), callsign.c_str()); + break; + case TL_FRANCAIS: + text.Printf(wxT("Connecte a %s"), callsign.c_str()); + break; + case TL_ITALIANO: + text.Printf(wxT("Connesso a %s"), callsign.c_str()); + break; + case TL_POLSKI: + text.Printf(wxT("Polaczony z %s"), callsign.c_str()); + break; + case TL_ESPANOL: + text.Printf(wxT("Enlazado %s"), callsign.c_str()); + break; + case TL_SVENSKA: + text.Printf(wxT("Lankad till %s"), callsign.c_str()); + break; + case TL_NEDERLANDS_NL: + case TL_NEDERLANDS_BE: + text.Printf(wxT("Gelinkt met %s"), callsign.c_str()); + break; + case TL_NORSK: + text.Printf(wxT("Tilkoblet %s"), callsign.c_str()); + break; + case TL_PORTUGUES: + text.Printf(wxT("Conectado a %s"), callsign.c_str()); + break; + default: + text.Printf(wxT("Linked to %s"), callsign.c_str()); + break; + } + + if (direction == DIR_OUTGOING) { + suspendLinks(); + + m_linkStatus = LS_LINKED_CCS; + m_linkRepeater = callsign; + m_queryTimer.stop(); + + CTextData textData(m_linkStatus, callsign, text, m_address, m_port); + m_repeaterHandler->writeText(textData); + + m_infoAudio->setStatus(m_linkStatus, m_linkRepeater, text); + triggerInfo(); + } else { + CTextData textData(m_linkStatus, m_linkRepeater, text, m_address, m_port, true); + m_repeaterHandler->writeText(textData); + + m_infoAudio->setTempStatus(LS_LINKED_CCS, callsign, text); + triggerInfo(); + } +} + +void CRepeaterHandler::ccsLinkEnded(const wxString&, DIRECTION direction) +{ + wxString tempText; + wxString text; + + switch (m_language) { + case TL_DEUTSCH: + text = wxT("Nicht verbunden"); + tempText = wxT("CCS ist beendet"); + break; + case TL_DANSK: + text = wxT("Ikke forbundet"); + tempText = wxT("CCS er afsluttet"); + break; + case TL_FRANCAIS: + text = wxT("Non connecte"); + tempText = wxT("CCS a pris fin"); + break; + case TL_ITALIANO: + text = wxT("Non connesso"); + tempText = wxT("CCS e finita"); + break; + case TL_POLSKI: + text = wxT("Nie polaczony"); + tempText = wxT("CCS zakonczyl"); + break; + case TL_ESPANOL: + text = wxT("No enlazado"); + tempText = wxT("CCS ha terminado"); + break; + case TL_SVENSKA: + text = wxT("Ej lankad"); + tempText = wxT("CCS har upphort"); + break; + case TL_NEDERLANDS_NL: + case TL_NEDERLANDS_BE: + text = wxT("Niet gelinkt"); + tempText = wxT("CCS is afgelopen"); + break; + case TL_NORSK: + text = wxT("Ikke linket"); + tempText = wxT("CCS er avsluttet"); + break; + case TL_PORTUGUES: + text = wxT("Desconectado"); + tempText = wxT("CCS terminou"); + break; + default: + text = wxT("Not linked"); + tempText = wxT("CCS has ended"); + break; + } + + if (direction == DIR_OUTGOING) { + m_linkStatus = LS_NONE; + m_linkRepeater.Clear(); + m_queryTimer.stop(); + + bool res = restoreLinks(); + if (!res) { + CTextData textData1(m_linkStatus, m_linkRepeater, tempText, m_address, m_port, true); + m_repeaterHandler->writeText(textData1); + + CTextData textData2(m_linkStatus, m_linkRepeater, text, m_address, m_port); + m_repeaterHandler->writeText(textData2); + + m_infoAudio->setStatus(m_linkStatus, m_linkRepeater, text); + m_infoAudio->setTempStatus(m_linkStatus, m_linkRepeater, tempText); + triggerInfo(); + } + } else { + CTextData textData(m_linkStatus, m_linkRepeater, tempText, m_address, m_port, true); + m_repeaterHandler->writeText(textData); + + m_infoAudio->setTempStatus(m_linkStatus, m_linkRepeater, tempText); + triggerInfo(); + } +} + +void CRepeaterHandler::ccsLinkFailed(const wxString& dtmf, DIRECTION direction) +{ + wxString tempText; + wxString text; + + switch (m_language) { + case TL_DEUTSCH: + text = wxT("Nicht verbunden"); + tempText.Printf(wxT("%s unbekannt"), dtmf.c_str()); + break; + case TL_DANSK: + text = wxT("Ikke forbundet"); + tempText.Printf(wxT("%s unknown"), dtmf.c_str()); + break; + case TL_FRANCAIS: + text = wxT("Non connecte"); + tempText.Printf(wxT("%s inconnu"), dtmf.c_str()); + break; + case TL_ITALIANO: + text = wxT("Non connesso"); + tempText.Printf(wxT("Sconosciuto %s"), dtmf.c_str()); + break; + case TL_POLSKI: + text = wxT("Nie polaczony"); + tempText.Printf(wxT("%s nieznany"), dtmf.c_str()); + break; + case TL_ESPANOL: + text = wxT("No enlazado"); + tempText.Printf(wxT("Desconocido %s"), dtmf.c_str()); + break; + case TL_SVENSKA: + text = wxT("Ej lankad"); + tempText.Printf(wxT("%s okand"), dtmf.c_str()); + break; + case TL_NEDERLANDS_NL: + case TL_NEDERLANDS_BE: + text = wxT("Niet gelinkt"); + tempText.Printf(wxT("%s bekend"), dtmf.c_str()); + break; + case TL_NORSK: + text = wxT("Ikke linket"); + tempText.Printf(wxT("%s ukjent"), dtmf.c_str()); + break; + case TL_PORTUGUES: + text = wxT("Desconectado"); + tempText.Printf(wxT("%s desconhecido"), dtmf.c_str()); + break; + default: + text = wxT("Not linked"); + tempText.Printf(wxT("%s unknown"), dtmf.c_str()); + break; + } + + if (direction == DIR_OUTGOING) { + m_linkStatus = LS_NONE; + m_linkRepeater.Clear(); + m_queryTimer.stop(); + + bool res = restoreLinks(); + if (!res) { + CTextData textData1(m_linkStatus, m_linkRepeater, tempText, m_address, m_port, true); + m_repeaterHandler->writeText(textData1); + + CTextData textData2(m_linkStatus, m_linkRepeater, text, m_address, m_port); + m_repeaterHandler->writeText(textData2); + + m_infoAudio->setStatus(m_linkStatus, m_linkRepeater, text); + m_infoAudio->setTempStatus(m_linkStatus, m_linkRepeater, tempText); + triggerInfo(); + } + } else { + CTextData textData(m_linkStatus, m_linkRepeater, tempText, m_address, m_port, true); + m_repeaterHandler->writeText(textData); + + m_infoAudio->setTempStatus(m_linkStatus, m_linkRepeater, tempText); + triggerInfo(); + } } void CRepeaterHandler::writeStatus(CStatusData& statusData) @@ -2591,6 +2900,8 @@ void CRepeaterHandler::suspendLinks() m_linkStatus = LS_NONE; m_linkRepeater.Clear(); m_linkReconnectTimer.stop(); + + m_ccsHandler->setReflector(); } bool CRepeaterHandler::restoreLinks() @@ -2633,3 +2944,27 @@ void CRepeaterHandler::triggerInfo() m_infoNeeded = false; } } + +bool CRepeaterHandler::isCCSCommand(const wxString& command) const +{ + if (command.IsSameAs(wxT("CA "))) + return true; + + wxChar c = command.GetChar(0U); + if (c != wxT('C')) + return false; + + c = command.GetChar(1U); + if (c < wxT('0') || c > wxT('9')) + return false; + + c = command.GetChar(2U); + if (c < wxT('0') || c > wxT('9')) + return false; + + c = command.GetChar(3U); + if (c < wxT('0') || c > wxT('9')) + return false; + + return true; +} diff --git a/Common/RepeaterHandler.h b/Common/RepeaterHandler.h index 0e1af0c..5871775 100644 --- a/Common/RepeaterHandler.h +++ b/Common/RepeaterHandler.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2015,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2010-2015 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 @@ -33,7 +33,9 @@ #include "HeaderLogger.h" #include "CallsignList.h" #include "DRATSServer.h" +#include "CCSCallback.h" #include "VersionUnit.h" +#include "CCSHandler.h" #include "StatusData.h" #include "APRSWriter.h" #include "HeardData.h" @@ -54,7 +56,7 @@ #include -class CRepeaterHandler : public IRepeaterCallback, public IReflectorCallback { +class CRepeaterHandler : public IRepeaterCallback, public IReflectorCallback, public ICCSCallback { public: static void initialise(unsigned int maxRepeaters); @@ -126,6 +128,10 @@ public: virtual void linkRefused(DSTAR_PROTOCOL protocol, const wxString& callsign); virtual bool linkFailed(DSTAR_PROTOCOL protocol, const wxString& callsign, bool isRecoverable); + virtual void ccsLinkMade(const wxString& callsign, DIRECTION direction); + virtual void ccsLinkFailed(const wxString& dtmf, DIRECTION direction); + virtual void ccsLinkEnded(const wxString& callsign, DIRECTION direction); + protected: CRepeaterHandler(const wxString& callsign, const wxString& band, const wxString& address, unsigned int port, HW_TYPE hwType, const wxString& reflector, bool atStartup, RECONNECT reconnect, bool dratsEnabled, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url, IRepeaterProtocolHandler* handler, unsigned char band1, unsigned char band2, unsigned char band3); virtual ~CRepeaterHandler(); @@ -259,6 +265,9 @@ private: // Poll timer CTimer m_pollTimer; + // CCS + CCCSHandler* m_ccsHandler; + // Reflector restoration wxString m_lastReflector; @@ -268,6 +277,7 @@ private: CTimer m_heardTimer; void g2CommandHandler(const wxString& callsign, const wxString& user, CHeaderData& header); + void ccsCommandHandler(const wxString& callsign, const wxString& user, const wxString& type); void reflectorCommandHandler(const wxString& callsign, const wxString& user, const wxString& type); void sendToOutgoing(const CHeaderData& header); void sendToOutgoing(const CAMBEData& data); @@ -288,6 +298,8 @@ private: bool restoreLinks(); void triggerInfo(); + + bool isCCSCommand(const wxString& command) const; }; #endif diff --git a/Data/CCS_Hosts.txt b/Data/CCS_Hosts.txt new file mode 100644 index 0000000..e572182 --- /dev/null +++ b/Data/CCS_Hosts.txt @@ -0,0 +1,15 @@ +CCS701 ccs701.xreflector.net +CCS702 ccs702.xreflector.net +CCS703 ccs703.xreflector.net +CCS704 ccs704.xreflector.net +CCS705 ccs705.xreflector.net +CCS706 ccs706.xreflector.net +CCS707 ccs707.xreflector.net +CCS710 ccs710.xreflector.net +CCS711 ccs711.xreflector.net +CCS713 ccs713.xreflector.net +CCS721 ccs721.xreflector.net +CCS722 ccs722.xreflector.net +CCS724 ccs724.xreflector.net +CCS728 ccs728.xreflector.net +CCS732 ccs732.xreflector.net diff --git a/Data/Makefile b/Data/Makefile index 7957e04..4350c30 100644 --- a/Data/Makefile +++ b/Data/Makefile @@ -1,5 +1,6 @@ install: install -d -g bin -o root -m 0775 $(DATADIR) + install -g bin -o root -m 0664 CCS_Hosts.txt $(DATADIR) install -g bin -o root -m 0664 DCS_Hosts.txt $(DATADIR) install -g bin -o root -m 0664 DExtra_Hosts.txt $(DATADIR) install -g bin -o root -m 0664 DPlus_Hosts.txt $(DATADIR) diff --git a/ircDDBGateway32.nsi b/ircDDBGateway32.nsi index ec64901..6300ec4 100644 --- a/ircDDBGateway32.nsi +++ b/ircDDBGateway32.nsi @@ -59,6 +59,7 @@ Section "Repeater Program Files" SecProgram File "C:\wxWidgets-3.0.4\lib\vc_dll\wxmsw30u_core_vc_custom.dll" File "CHANGES.txt" File "COPYING.txt" + File "Data\CCS_Hosts.txt" File "Data\DCS_Hosts.txt" File "Data\DExtra_Hosts.txt" File "Data\DPlus_Hosts.txt" diff --git a/ircDDBGateway64.nsi b/ircDDBGateway64.nsi index e5a7248..fb1b779 100644 --- a/ircDDBGateway64.nsi +++ b/ircDDBGateway64.nsi @@ -59,6 +59,7 @@ Section "Repeater Program Files" SecProgram File "C:\wxWidgets-3.0.4\lib\vc_x64_dll\wxmsw30u_core_vc_x64_custom.dll" File "CHANGES.txt" File "COPYING.txt" + File "Data\CCS_Hosts.txt" File "Data\DCS_Hosts.txt" File "Data\DExtra_Hosts.txt" File "Data\DPlus_Hosts.txt" From dd2760b4349722c7d4c58daf211ee4a80970d9d7 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 19 Sep 2018 21:05:28 +0100 Subject: [PATCH 2/2] Add GUI makefiles for the other GUI capable programs. --- MakefileGUI | 41 +++++++++++++++++++-------------------- RemoteControl/MakefileGUI | 18 +++++++++++++++++ StarNetServer/MakefileGUI | 18 +++++++++++++++++ TimeServer/MakefileGUI | 17 ++++++++++++++++ TimerControl/MakefileGUI | 18 +++++++++++++++++ 5 files changed, 91 insertions(+), 21 deletions(-) create mode 100644 RemoteControl/MakefileGUI create mode 100644 StarNetServer/MakefileGUI create mode 100644 TimeServer/MakefileGUI create mode 100644 TimerControl/MakefileGUI diff --git a/MakefileGUI b/MakefileGUI index a47a5be..6cd4315 100644 --- a/MakefileGUI +++ b/MakefileGUI @@ -12,10 +12,10 @@ export GUILIBS := $(shell wx-config --libs adv,core,base) export LIBS := $(shell wx-config --libs base) export LDFLAGS := -all: ircDDBGateway/ircddbgatewayd ircDDBGatewayConfig/ircddbgatewayconfig APRSTransmit/aprstransmitd RemoteControl/remotecontrold \ - StarNetServer/starnetserverd TextTransmit/texttransmitd TimerControl/timercontrold TimeServer/timeserverd VoiceTransmit/voicetransmitd +all: ircDDBGateway/ircddbgateway ircDDBGatewayConfig/ircddbgatewayconfig APRSTransmit/aprstransmitd RemoteControl/remotecontrol \ + StarNetServer/starnetserver TextTransmit/texttransmitd TimerControl/timercontrol TimeServer/timeserver VoiceTransmit/voicetransmitd -ircDDBGateway/ircddbgatewayd: GUICommon/GUICommon.a Common/Common.a ircDDB/IRCDDB.a +ircDDBGateway/ircddbgateway: GUICommon/GUICommon.a Common/Common.a ircDDB/IRCDDB.a make -C ircDDBGateway -f MakefileGUI ircDDBGatewayConfig/ircddbgatewayconfig: GUICommon/GUICommon.a Common/Common.a @@ -24,20 +24,20 @@ ircDDBGatewayConfig/ircddbgatewayconfig: GUICommon/GUICommon.a Common/Common.a APRSTransmit/aprstransmitd: Common/Common.a make -C APRSTransmit -RemoteControl/remotecontrold: Common/Common.a - make -C RemoteControl +RemoteControl/remotecontrol: Common/Common.a + make -C RemoteControl -f MakefileGUI -StarNetServer/starnetserverd: Common/Common.a ircDDB/IRCDDB.a - make -C StarNetServer +StarNetServer/starnetserver: Common/Common.a ircDDB/IRCDDB.a + make -C StarNetServer -f MakefileGUI TextTransmit/texttransmitd: Common/Common.a make -C TextTransmit -TimerControl/timercontrold: Common/Common.a GUICommon/GUICommon.a - make -C TimerControl +TimerControl/timercontrol: Common/Common.a GUICommon/GUICommon.a + make -C TimerControl -f MakefileGUI -TimeServer/timeserverd: Common/Common.a GUICommon/GUICommon.a - make -C TimeServer +TimeServer/timeserver: Common/Common.a GUICommon/GUICommon.a + make -C TimeServer -f MakefileGUI VoiceTransmit/voicetransmitd: Common/Common.a make -C VoiceTransmit @@ -55,11 +55,11 @@ install: all make -C Data install make -C APRSTransmit install make -C ircDDBGateway -f MakefileGUI install - make -C RemoteControl install - make -C StarNetServer install + make -C RemoteControl -f MakefileGUI install + make -C StarNetServer -f MakefileGUI install make -C TextTransmit install - make -C TimerControl install - make -C TimeServer install + make -C TimerControl -f MakefileGUI install + make -C TimeServer -f MakefileGUI install make -C VoiceTransmit install make -C ircDDBGatewayConfig install @@ -68,12 +68,11 @@ clean: make -C ircDDB clean make -C GUICommon clean make -C APRSTransmit clean - make -C ircDDBGateway clean - make -C RemoteControl clean - make -C StarNetServer clean + make -C ircDDBGateway -f MakefileGUI clean + make -C RemoteControl -f MakefileGUI clean + make -C StarNetServer -f MakefileGUI clean make -C TextTransmit clean - make -C TimerControl clean - make -C TimeServer clean + make -C TimerControl -f MakefileGUI clean + make -C TimeServer -f MakefileGUI clean make -C VoiceTransmit clean make -C ircDDBGatewayConfig clean - diff --git a/RemoteControl/MakefileGUI b/RemoteControl/MakefileGUI new file mode 100644 index 0000000..316607f --- /dev/null +++ b/RemoteControl/MakefileGUI @@ -0,0 +1,18 @@ +OBJECTS = RemoteControlApp.o RemoteControlCallsignData.o RemoteControlConfig.o RemoteControlFrame.o RemoteControlLinkData.o \ + RemoteControlPreferences.o RemoteControlRemoteControlHandler.o RemoteControlRemoteSet.o RemoteControlRepeaterData.o \ + RemoteControlRepeaterPanel.o RemoteControlStarNetGroup.o RemoteControlStarNetPanel.o RemoteControlStarNetUser.o + +all: remotecontrol + +remotecontrol: $(OBJECTS) + $(CXX) $(OBJECTS) ../GUICommon/GUICommon.a ../Common/Common.a $(LDFLAGS) $(GUILIBS) -o remotecontrol + +%.o: %.cpp + $(CXX) $(CFLAGS) -I../Common -I../GUICommon -c -o $@ $< + +install: + install -g bin -o root -m 0775 remotecontrol $(BINDIR) + +clean: + $(RM) remotecontrol *.o *.d *.bak *~ + diff --git a/StarNetServer/MakefileGUI b/StarNetServer/MakefileGUI new file mode 100644 index 0000000..9073dab --- /dev/null +++ b/StarNetServer/MakefileGUI @@ -0,0 +1,18 @@ +OBJECTS = StarNetServerApp.o StarNetServerCallsignSet.o StarNetServerConfig.o StarNetServerFrame.o StarNetServerIrcDDBSet.o \ + StarNetServerLogRedirect.o StarNetServerMiscellaneousSet.o StarNetServerPreferences.o StarNetServerThread.o \ + StarNetServerThreadHelper.o + +all: starnetserver + +starnetserver: $(OBJECTS) + $(CXX) $(OBJECTS) ../GUICommon/GUICommon.a ../Common/Common.a ../ircDDB/IRCDDB.a $(LDFLAGS) $(GUILIBS) -o starnetserver + +%.o: %.cpp + $(CXX) $(CFLAGS) -I../Common -I../GUICommon -I../ircDDB -c -o $@ $< + +install: + install -g bin -o root -m 0775 starnetserver $(BINDIR) + +clean: + $(RM) starnetserver *.o *.d *.bak *~ + diff --git a/TimeServer/MakefileGUI b/TimeServer/MakefileGUI new file mode 100644 index 0000000..9ac2e11 --- /dev/null +++ b/TimeServer/MakefileGUI @@ -0,0 +1,17 @@ +OBJECTS = TimeServerApp.o TimeServerAnnouncementsSet.o TimeServerConfig.o TimeServerFrame.o TimeServerGatewaySet.o TimeServerLogRedirect.o \ + TimeServerPreferences.o TimeServerThread.o TimeServerThreadHelper.o + +all: timeserver + +timeserver: $(OBJECTS) + $(CXX) $(OBJECTS) ../GUICommon/GUICommon.a ../Common/Common.a $(LDFLAGS) $(GUILIBS) -o timeserver + +%.o: %.cpp + $(CXX) $(CFLAGS) -I../Common -I../GUICommon -c -o $@ $< + +install: + install -g bin -o root -m 0775 timeserver $(BINDIR) + +clean: + $(RM) timeserver *.o *.d *.bak *~ + diff --git a/TimerControl/MakefileGUI b/TimerControl/MakefileGUI new file mode 100644 index 0000000..0012a10 --- /dev/null +++ b/TimerControl/MakefileGUI @@ -0,0 +1,18 @@ +OBJECTS = TimerControlApp.o TimerControlConfig.o TimerControlFrame.o TimerControlItemFile.o TimerControlPreferences.o \ + TimerControlRemoteControlHandler.o TimerControlRemoteSet.o TimerControlRepeaterPanel.o TimerControlThread.o \ + TimerControlThreadHelper.o + +all: timercontrol + +timercontrol: $(OBJECTS) + $(CXX) $(OBJECTS) ../GUICommon/GUICommon.a ../Common/Common.a $(LDFLAGS) $(GUILIBS) -o timercontrol + +%.o: %.cpp + $(CXX) $(CFLAGS) -I../Common -I../GUICommon -c -o $@ $< + +install: + install -g bin -o root -m 0775 timercontrol $(BINDIR) + +clean: + $(RM) timercontrol *.o *.d *.bak *~ +